任務(Task)
Task作為C#異步的核心,Task位于System.Threading.Tasks命名空間下。
創建任務的基本原理是使用線程池,也就是說任務最終也是要交給線程去執行的。但是微軟優化了任務的線程池,使線程的控制更加精準和高效.
task默認是后臺線程,而thread默認是前臺線程.可以設置task為前臺線程,Thread.CurrentThread.IsBackground,將IsBackground設置為false即可.
Task出現之前,微軟的多線程處理方式有:Thread→ThreadPool→委托的異步調用,雖然也可以基本業務需要的多線程場景,但它們在多個線程的等待處理方面、資源占用方面、線程延續和阻塞方面、線程的取消方面等都顯得比較笨拙,ThreadPool相比Thread來說具備了很多優勢,但是ThreadPool卻又存在一些使用上的不方便。比如:
ThreadPool不支持線程的取消、完成、失敗通知等交互性操作;
ThreadPool不支持線程執行的先后次序;
Task在ThreadPool的基礎上進行的封裝,Task的控制和擴展性很強,在線程的延續、阻塞、取消、超時等方面遠勝于Thread和ThreadPool。
新建并啟動task:
方式1:TaskFactory工廠
如果你想建立一個Task并且立即執行它,使用Task.Factory.StartNew(),這樣做性能更好。
Task.Factory.StartNew(() =>
{
Console.WriteLine("Main2 Thread" + Thread.CurrentThread.ManagedThreadId);
});
//帶有參數,返回值任務
Task<int>.Factory.StartNew(new Func<object, int>(test3), i);
static int test3(object obj)
{
????int i = (int)obj;
????Thread.Sleep(3000);
????return i + i;
}
方式2:調用Start方法
如果你想建立一個Task,想在合適的時候執行它,那么使用Task構造器做初始化,使用Start函數啟動任務。
//不帶返回值,無參數的任務
Task task = new Task(() =>
{
????Console.WriteLine("Main1 Thread" + Thread.CurrentThread.ManagedThreadId);
});
task.Start();
//帶返回值,無參數的任務
Task<int> task1 = new Task<int>(() =>
{
????Console.WriteLine("Main2 Thread" + Thread.CurrentThread.ManagedThreadId);
?????return 0;
});
task1.Start();
//帶有參數,無返回值任務
Action<object> action = (a) =>
{
????Console.WriteLine("The delegate method is executed,state is :" + a);
};
object obj2 = "success";
Task task2 = new Task(action, obj2);
task1.Start();
//帶有參數,返回值任務
object i = 5;
Task<int> task3 = new Task<int>(new Func<object, int>(test3),i);
task3.Start();
static int test3(object obj)
{
????int i = (int)obj;
????Thread.Sleep(3000);
????return i + i;
}
方式3:靜態方法Run
//將任務放在線程池隊列,返回并啟動一個Task.task無參數,無返回值
Task task4 = Task.Run(() =>
{
????Console.WriteLine("Main4 Thread" + Thread.CurrentThread.ManagedThreadId);
?});
?//將任務放在線程池隊列,返回并啟動一個Task.task無參數,帶返回值
????????static int test5()
????????{ ??
????????????Thread.Sleep(3000);
????????????return 0;
????????}
?Task<int> task5 = Task.Run<int>(new Func<int>(test5));
求返回值
當task有返回值時,可通過public TResult Result { get; }? Result 字段獲取返回值。
如:int? nret=?task5.Result ;
使用Result 字段,會導致當前線程阻塞,直到task結束返回。
延伸到await :await 后面代碼會阻塞,但當前線程不會阻塞。
等待task:
使用wait或WaitAll、WaitAny函數等待,可以設置等待時間。
Task task = new Task(() =>
{
????Console.WriteLine("Main1 Thread" + Thread.CurrentThread.ManagedThreadId);
});
task.Start();
task.Wait(1000);
WaitAll為靜態函數,可以設置等待多個任務結束,所有任務結束才返回。
Task<int> task1 = new Task<int>(() =>
{
????Console.WriteLine("Main2 Thread" + Thread.CurrentThread.ManagedThreadId);
?????return 0;
});
task1.Start();
Task.WaitAll(task,task1 );
WaitAny靜態函數,可以設置等待多個任務結束,只要一個任務結束就返回。
Task<int> task1 = new Task<int>(() =>
{
????Console.WriteLine("Main2 Thread" + Thread.CurrentThread.ManagedThreadId);
?????return 0;
});
task1.Start();
Task.WaitAny(task,task1 );
WhenAny
WhenAny:與ContinueWith配合,線程列表中任何一個執行完畢,則繼續ContinueWith中的任務(開啟新線程,不阻塞主線程)
Action<string,int> log = (name,time) =>
????{
????????Console.WriteLine($"{name}任務開始...");
????????Thread.Sleep(time);
????????Console.WriteLine($"{name}任務結束!");
????};
????List<Task> tasks = new List<Task>
????{
????????Task.Run(() => log("張三",3000)),
????????Task.Run(() => log("李四",1000)),
????????Task.Run(() => log("王五",2000))
};
Task.WhenAny(tasks.ToArray()).ContinueWith(x => Console.WriteLine("某個Task執行完畢"));
連續任務
可以指定在任務完成之后,應開始運行之后另一個特定任務。ContinueWith是Task根據其自身狀況,決定后續應該作何操作。
????????????var testTask = new Task(() =>
????????????{
????????????????Console.WriteLine("task start");
????????????????System.Threading.Thread.Sleep(2000);
????????????});
????????????testTask.Start();
????????????var resultTest = testTask.ContinueWith<string>( task6 => {
????????????????Console.WriteLine("testTask end");
????????????????return "end";
????????????});
????????????Console.WriteLine(resultTest.Result);
取消任務
使用CancellationTokenSource:創建一個CancellationTokenSource對象,并將其傳遞給要取消的任務。然后,調用CancellationTokenSource的Cancel方法來觸發任務的取消。
????????????var tokenSource = new CancellationTokenSource();
????????????var token = tokenSource.Token;
????????????var task7 = Task.Factory.StartNew(() =>
????????????{
????????????????????System.Threading.Thread.Sleep(4000);
????????????????????if (token.IsCancellationRequested)
????????????????????{
????????????????????????Console.WriteLine("Abort mission success!");
????????????????????????return;
????????????????????} ??
????????????}, token);
????????????//注冊cancel后要執行的代碼
????????????token.Register(() =>
????????????{
????????????????Console.WriteLine("Canceled");
????????????});
????????????Console.WriteLine("Press enter to cancel task...");
????????????//調用取消
????????????tokenSource.Cancel();