Detaillierte Erläuterung des grafischen Codes von Thread, Task, Async/Await, IAsyncResult in C#

黄舟
Freigeben: 2017-03-29 11:28:10
Original
3238 Leute haben es durchsucht

In diesem Artikel werden hauptsächlich die relevanten Kenntnisse zu Thread, Task, Async/Await und IAsyncResult in C# vorgestellt. Es hat einen gewissen Referenzwert, werfen wir einen Blick mit dem Editor unten

Apropos asynchron, Thread, Task, async/await, IAsyncResult, diese Dinge sind heute definitiv unvermeidlich

1. Thread

Die Bedeutung von Multithreading besteht darin, dass in einer Anwendung mehrere Ausführungsteile gleichzeitig ausgeführt werden können ( Für Beispiel: io, Datenbankoperationen ) oder das Warten auf eine Antwort (z. B als WCF-Kommunikation) Der Vorgang kann durch separates Starten des Hintergrundthreads ausgeführt werden, sodass der Hauptthread nicht blockiert wird und weiter ausgeführt werden kann. Warten Sie, bis der Hintergrundthread abgeschlossen ist, benachrichtigen Sie dann den Hauptthread und führen Sie dann die entsprechenden Schritte aus Operation

in C#! Es ist relativ einfach, einen neuen Thread in

static void Main(string[] args)
{
 Console.WriteLine("主线程开始");
 //IsBackground=true,将其设置为后台线程
 Thread t = new Thread(Run) { IsBackground = true };
 t.Start();
   Console.WriteLine("主线程在做其他的事!");
 //主线程结束,后台线程会自动结束,不管有没有执行完成
 //Thread.Sleep(300);
 Thread.Sleep(1500);
 Console.WriteLine("主线程结束");
}
static void Run()
{
 Thread.Sleep(700);
 Console.WriteLine("这是后台线程调用");
}
Nach dem Login kopieren

zu starten. Das Ausführungsergebnis ist wie folgt:

Sie können sehen, dass nach dem Starten des Hintergrundthreads der Hauptthread weiterhin ausgeführt wird

1.1 Thread Pool

Stellen Sie sich vor, es gäbe einen Ist es für die Verarbeitung von HTTP-Anfragen im Hintergrund der Website erforderlich, für jede Anfrage einen Hintergrund-Thread zu erstellen? Dies ist offensichtlich ungeeignet Der häufige Erstellungsprozess wirkt sich auch erheblich auf die Geschwindigkeit aus. Der Zweck des Pools besteht darin, einen Thread-Pool zu bilden (mit mehreren Threads darin). Zur Verarbeitung gilt: Wenn sich im Thread-Pool inaktive Threads befinden (nach Abschluss der vorherigen Aufgabe wird der Thread nicht recycelt und in den Leerlaufzustand versetzt), rufen Sie dann direkt die Thread-Ausführung im Thread-Pool auf (z. B. das Application-Objekt im asp.net-Verarbeitungsmechanismus),

Verwendungsbeispiel:

for (int i = 0; i < 10; i++)
{
 ThreadPool.QueueUserWorkItem(m =>
 {
  Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
 });
}
Console.Read();
Nach dem Login kopieren

Laufergebnis:

Sie können sehen, dass, obwohl es 10 Mal ausgeführt wurde, 10 Threads nicht erstellt wurden

1.2 Semaphore

Semaphore ist für die Koordination von Threads verantwortlich und kann die Anzahl der Threads begrenzen, die auf eine bestimmte Ressource zugreifen

Hier ist ein einfaches Beispiel für die Verwendung der SemaphoreSlim-Klasse:

 static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三个线程同时访问
static void Main(string[] args)
{
 for (int i = 0; i < 10; i++)
 {
  new Thread(SemaphoreTest).Start();
 }
 Console.Read();
}
static void SemaphoreTest()
{
 semLim.Wait();
 Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "开始执行");
 Thread.Sleep(2000);
 Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "执行完毕");
 semLim.Release();
}
Nach dem Login kopieren

Die Ausführung Die Ergebnisse lauten wie folgt:

Wie Sie sehen können, werden am Anfang nur drei Threads ausgeführt. Wenn ein Thread ausgeführt und freigegeben wird, gibt es einen Neuer Thread zum Ausführen der Methode.

Zusätzlich zur Semaphore-Klasse können Sie auch die Semaphore-Klasse verwenden, die sich flexibler anfühlt. Sie können sie durchsuchen. Hier wird es keine Demonstration geben.

2.Task

Task wurde in .NET4.0 hinzugefügt. Es verfügt über ähnliche Funktionen wie der Thread-Pool ThreadPool. Es wird aus dem Thread-Pool des aufrufenden Threads gezogen und Thread erstellt bei jeder Instanziierung einen neuen Thread.

Console.WriteLine("主线程启动");
//Task.Run启动一个线程
//Task启动的是后台线程,要在主线程中等待后台线程执行完毕,可以调用Wait方法
//Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); });
Task task = Task.Run(() => { 
 Thread.Sleep(1500);
 Console.WriteLine("task启动");
});
Thread.Sleep(300);
task.Wait();
Console.WriteLine("主线程结束");
Nach dem Login kopieren

Die Ausführungsergebnisse lauten wie folgt:

Methode zum Starten einer neuen Aufgabe: Task.Run() oder Task.Factory.StartNew( ), open Es ist ein Hintergrundthread

Um darauf zu warten, dass der Hintergrundthread die Ausführung im Hauptthread abschließt, können Sie die Wait-Methode verwenden (sie wird synchron ausgeführt). Ohne Warten wird es asynchron ausgeführt.

Aufgabe und Thread vergleichen:

static void Main(string[] args)
{
 for (int i = 0; i < 5; i++)
 {
  new Thread(Run1).Start();
 }
 for (int i = 0; i < 5; i++)
 {
  Task.Run(() => { Run2(); });
 }
}
static void Run1()
{
 Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
static void Run2()
{
 Console.WriteLine("Task调用的Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
Nach dem Login kopieren

Ausführungsergebnis:

Es ist ersichtlich, dass die direkte Verwendung von Thread 5 Threads öffnet , 3 mit Task geöffnet (unter Verwendung des Thread-Pools)!

2.1 Task

Task ist eine Aufgabe mit einem Rückgabewert und TResult ist der Rückgabewerttyp.

Console.WriteLine("主线程开始");
//返回值类型为string
Task<string> task = Task<string>.Run(() => {
 Thread.Sleep(2000); 
 return Thread.CurrentThread.ManagedThreadId.ToString(); 
});
//会等到task执行完毕才会输出;
Console.WriteLine(task.Result);
Console.WriteLine("主线程结束");
Nach dem Login kopieren

Laufendes Ergebnis:

Der Rückgabewert kann über task.Result abgerufen werden, wenn die Ausführung des Hintergrundthreads zum Zeitpunkt des Werts noch nicht abgeschlossen ist erhalten, Es wird auf den Abschluss seiner Ausführung warten!

Eine kurze Erwähnung:

Aufgabenaufgaben können über die Klasse CancellationTokenSource abgebrochen werden. Ich habe das Gefühl, dass sie nicht häufig verwendet wird und die Verwendung relativ einfach ist. Wenn Sie interessiert sind, können Sie sie durchsuchen !

3. async/await

async/await wurde in C# 5.0 eingeführt, erste Verwendung:

static void Main(string[] args)
{
 Console.WriteLine("-------主线程启动-------");
 Task<int> task = GetStrLengthAsync();
 Console.WriteLine("主线程继续执行");
 Console.WriteLine("Task返回的值" + task.Result);
 Console.WriteLine("-------主线程结束-------");
}
static async Task<int> GetStrLengthAsync()
{
 Console.WriteLine("GetStrLengthAsync方法开始执行");
 //此处返回的<string>中的字符串类型,而不是Task<string>
 string str = await GetString();
 Console.WriteLine("GetStrLengthAsync方法执行结束");
 return str.Length;
}
static Task<string> GetString()
{
   //Console.WriteLine("GetString方法开始执行")
 return Task<string>.Run(() =>
 {
  Thread.Sleep(2000);
  return "GetString的返回值";
 });
}
Nach dem Login kopieren

async wird verwendet. Ändern Sie die Methode um anzugeben, dass die Methode asynchron ist. Der Rückgabetyp der deklarierten Methode muss sein: void, Task oder Task.

await必须用来修饰Task或Task,而且只能出现在已经用async关键字修饰的异步方法中。通常情况下,async/await成对出现才有意义,

看看运行结果:

可以看出来,main函数调用GetStrLengthAsync方法后,在await之前,都是同步执行的,直到遇到await关键字,main函数才返回继续执行。

那么是否是在遇到await关键字的时候程序自动开启了一个后台线程去执行GetString方法呢?

现在把GetString方法中的那行注释加上,运行的结果是:

大家可以看到,在遇到await关键字后,没有继续执行GetStrLengthAsync方法后面的操作,也没有马上反回到main函数中,而是执行了GetString的第一行,以此可以判断await这里并没有开启新的线程去执行GetString方法,而是以同步的方式让GetString方法执行,等到执行到GetString方法中的Task.Run()的时候才由Task开启了后台线程!

那么await的作用是什么呢?

可以从字面上理解,上面提到task.wait可以让主线程等待后台线程执行完毕,await和wait类似,同样是等待,等待Task.Run()开始的后台线程执行完毕,不同的是await不会阻塞主线程,只会让GetStrLengthAsync方法暂停执行。

那么await是怎么做到的呢?有没有开启新线程去等待?

只有两个线程(主线程和Task开启的线程)!至于怎么做到的(我也不知道......>_<),大家有兴趣的话研究下吧!

4.IAsyncResult

IAsyncResult自.NET1.1起就有了,包含可异步操作的方法的类需要实现它,Task类就实现了该接口

在不借助于Task的情况下怎么实现异步呢?

class Program
{
 static void Main(string[] args)
 {
  Console.WriteLine("主程序开始--------------------");
  int threadId;
  AsyncDemo ad = new AsyncDemo();
  AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

  IAsyncResult result = caller.BeginInvoke(3000,out threadId, null, null);
  Thread.Sleep(0);
  Console.WriteLine("主线程线程 {0} 正在运行.",Thread.CurrentThread.ManagedThreadId)
  //会阻塞线程,直到后台线程执行完毕之后,才会往下执行
  result.AsyncWaitHandle.WaitOne();
  Console.WriteLine("主程序在做一些事情!!!");
  //获取异步执行的结果
  string returnValue = caller.EndInvoke(out threadId, result);
  //释放资源
  result.AsyncWaitHandle.Close();
  Console.WriteLine("主程序结束--------------------");
  Console.Read();
 }
}
public class AsyncDemo
{
 //供后台线程执行的方法
 public string TestMethod(int callDuration, out int threadId)
 {
  Console.WriteLine("测试方法开始执行.");
  Thread.Sleep(callDuration);
  threadId = Thread.CurrentThread.ManagedThreadId;
  return String.Format("测试方法执行的时间 {0}.", callDuration.ToString());
 }
}
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
Nach dem Login kopieren

关键步骤就是红色字体的部分,运行结果:

和Task的用法差异不是很大!result.AsyncWaitHandle.WaitOne()就类似Task的Wait。

5.Parallel

最后说一下在循环中开启多线程的简单方法:

Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (int i = 1; i <= 10; i++)
{
 Console.Write(i + ",");
 Thread.Sleep(1000);
}
watch1.Stop();
Console.WriteLine(watch1.Elapsed);
Stopwatch watch2 = new Stopwatch();
watch2.Start();
//会调用线程池中的线程
Parallel.For(1, 11, i =>
{
 Console.WriteLine(i + ",线程ID:" + Thread.CurrentThread.ManagedThreadId);
 Thread.Sleep(1000);
});
watch2.Stop();
Console.WriteLine(watch2.Elapsed);
Nach dem Login kopieren

运行结果:

循环List

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 };
Parallel.ForEach<int>(list, n =>
{
 Console.WriteLine(n);
 Thread.Sleep(1000);
});
Nach dem Login kopieren

执行Action[]数组里面的方法:

Action[] actions = new Action[] { 
 new Action(()=>{
  Console.WriteLine("方法1");
 }),
 new Action(()=>{
  Console.WriteLine("方法2");
 })
};
Parallel.Invoke(actions);
Nach dem Login kopieren

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des grafischen Codes von Thread, Task, Async/Await, IAsyncResult in C#. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!