首頁  >  文章  >  後端開發  >  C#中Thread,Task,Async/Await,IAsyncResult的圖文程式碼詳解

C#中Thread,Task,Async/Await,IAsyncResult的圖文程式碼詳解

黄舟
黄舟原創
2017-03-29 11:28:103239瀏覽

本文主要介紹了C#中 Thread,Task,Async/Await,IAsyncResult的相關知識。具有一定的參考價值,下面跟著小編一起來看下吧

說起異步,Thread,Task,async/await,IAsyncResult 這些東西肯定是繞不開的,今天就來依次聊聊他們

1.執行緒(Thread)

多執行緒的意義在於一個應用程式中,有多個執行部分可以同時執行;對於比較耗時的操作(例如io,資料庫操作),或等待回應(如WCF通訊)的操作,可以單獨開啟後台執行緒來執行,這樣主執行緒就不會阻塞,可以繼續往下執行;等到後台執行緒執行完畢,再通知主執行緒,然後做出對應操作!中開啟新執行緒比較簡單

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("这是后台线程调用");
}

執行結果如下圖,

#可以看到在啟動後台執行緒之後,主執行緒繼續往下執行了,並沒有等到後台執行緒執行完之後。對於HTTP請求的處理,那是不是要對每一個請求創建一個後台線程呢?為了解決這個問題,把創建的線程存起來,形成一個線程池(裡面有多個線程),當要處理任務時,若線程池中有空閒線程(前一個任務執行完成後,線程不會被回收,會被設定為空閒狀態),則直接呼叫執行緒池中的執行緒執行(例

asp.net

處理機制中的Application

物件

),使用例子:

for (int i = 0; i < 10; i++)
{
 ThreadPool.QueueUserWorkItem(m =>
 {
  Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
 });
}
Console.Read();
執行結果:

可以看到,雖然執行了10次,但並沒有建立10個執行緒。

## 1.2 信號量(Semaphore)

 Semaphore負責協調線程,可以限制對某一資源訪問的線程數量

 這裡對SemaphoreSlim類別的用法做一個簡單的例子:

 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();
}

執行結果如下:

#可以看到,剛開始只有三個執行緒在執行,當一個線程執行完畢並釋放之後,才會有新的線程來執行方法!做示範了!

2.Task

Task是.NET4.0加入的,跟執行緒池ThreadPool的功能類似,用Task開啟新任務時,會從執行緒池中呼叫線程,而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("主线程结束");

執行結果如下:

開啟新任務的方法:Task.Run()或Task.Factory.StartNew(),開啟的是後台執行緒

要在主執行緒等待後台執行緒執行完畢,可以使用Wait方法(會以同步的方式來執行)。不用Wait則會以非同步的方式來執行。

比較一下Task和Thread:

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);
}

執行結果:

#可以看出來,直接用Thread會開啟5個線程,用Task(用了線程池)開啟了3個!

2.1 Taskb54c2c292509147c0b54128f4eb90887

#Taskb54c2c292509147c0b54128f4eb90887就是有回傳值的Task,TResult就是回傳值類型。

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

運行結果:

透過task.Result可以取到回傳值,若取值的時候,後台執行緒還沒執行完,則會等待其執行完畢!

簡單提一下:Task任務可以透過CancellationTokenSource類別來取消,感覺用得不多,用法比較簡單,感興趣的話可以搜一下!

3. async/await

async/await是C#5.0中推出的,先上用法:

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的返回值";
 });
}
async用來修飾方法,表示這個方法是異步的,聲明的方法的回傳類型必須為:void,Task或Taskb54c2c292509147c0b54128f4eb90887。

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

看看运行结果:

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

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

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

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

那么await的作用是什么呢?

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

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

只有两个线程(主线程和Task开启的线程)!至于怎么做到的(我也不知道......>_03065e15d4ebf3426e4f9cf8ae830fea:

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);
});

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

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

以上是C#中Thread,Task,Async/Await,IAsyncResult的圖文程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn