C#多線程編程のTask(任務全面解析)


Task是.NET4.0加入的,跟線程池ThreadPool的功能類似,用Task開啟新任務時,會從線程池中調用線程,而Thread每次實例化都會創建一個新的線程。

 我們可以說Task是一種基於任務的編程模型。它與thread的主要區別是,它更加方便對線程進程調度和獲取線程的執行結果。

 

Task類和Task<TResult>類
前者接收的是Action委托類型
后者接收的是Func<TResult>委托類型

 

任務和線程的區別:

1、任務是架構在線程之上的,也就是說任務最終還是要拋給線程去執行。

2、任務跟線程不是一對一的關系,比如開10個任務並不是說會開10個線程,這一點任務有點類似線程池,但是任務相比
線程池有很小的開銷和精確的控制。

一、Task的創建

1、直接創建

var task1 = new Task(() =>
{
         Console.WriteLine("Begin");
         System.Threading.Thread.Sleep(5000);
         Console.WriteLine("Finish");
   });
Console.WriteLine("Before start:" + task1.Status);
task1.Start();

2、工廠創建

Task.Factory.StartNew(()={

});

3、4.5以后Run運行

Task.Run(()=>{

});

4、一種方便獲取返回值的方式

static void Main(string[] args) 
        { 
            var tcs = new TaskCompletionSource<int>(); 
            new Thread(() => { 
                Thread.Sleep(5000); 
                int i = Enumerable.Range(1, 100).Sum(); 
                tcs.SetResult(i); }).Start();//線程把運行計算結果,設為tcs的Result。 
              Task<int> task = tcs.Task; 
              Console.WriteLine(task.Result); //此處會阻塞,直到匿名線程調用tcs.SetResult(i)完畢 
        } 

二、細節解釋

看下面代碼:

namespace WpfApplication6
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
            ConsoleManager.Show();//打開控制台窗口  
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Console.WriteLine("主線程啟動");
            Task task = Task.Run(() => {
                Thread.Sleep(1500);
                Console.WriteLine("task啟動");
            });
            Thread.Sleep(300);
            task.Wait();
            Console.WriteLine("主線程結束");
        }

    }
}

  

結果:

分析:

開啟新任務的方法:Task.Run()或者Task.Factory.StartNew(),開啟的是后台線程

要在主線程中等待后台線程執行完畢,可以使用Wait方法(會以同步的方式來執行)。不用Wait則會以異步的方式來執行。

thread和Task的區別,thread new多少個就會創建多少個線程,而task是利用線程池中的線程。

 

task<TResult>就是有返回值的Task,TResult就是返回值類型。示例:

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            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類來取消。

三、Task的其他方法

Task.Wait();    //阻塞當前線程
Task.WaitAll(); //阻塞當前線程直到所有的任務執行完畢
Task.WaitAny(); //阻塞當前線程直到有任意一個任務執行完畢


Task.ContinueWith(
task=>{

});              //執行完上一個任務后繼續執行,並將上一個任務(包括結果)傳遞給下一個代碼塊


一種是使用GetAwaiter方法。GetAwaiter方法返回一個TaskAwaiter結構,該結構有一個OnCompleted事件,只需對

OnCompleted事件賦值,即可在完成后調用該事件。 

static void Main(string[] args) 
        { 
            Task<int> Task1 = Task.Run<int>(() => { return Enumerable.Range(1, 100).Sum(); }); 
            var awaiter = Task1.GetAwaiter(); 
            awaiter.OnCompleted(() => 
            { 
                Console.WriteLine("Task1 finished"); 
                int result = awaiter.GetResult(); 
                Console.WriteLine(result); // Writes result 
            }); 
            Thread.Sleep(1000); 
        } 

四、任務的中斷

            var tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;
            var task = Task.Factory.StartNew(() =>
            {
                for (var i = 0; i < 1000; i++)
                {
                    System.Threading.Thread.Sleep(1000);
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Abort mission success!");
                        return;
                    }
                }
            }, token);

            //注冊cancel后要執行的代碼
            token.Register(() =>
            {
                Console.WriteLine("Canceled");
            });
            Console.WriteLine("Press enter to cancel task...");
            Console.ReadKey();
            //調用取消
            tokenSource.Cancel();

五、任務的中斷取消

            var tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;
            var task = Task.Factory.StartNew(() =>
            {
                for (var i = 0; i < 1000; i++)
                {
                    System.Threading.Thread.Sleep(1000);
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Abort mission success!");
                        return;
                    }
                }
            }, token);

            //注冊cancel后要執行的代碼
            token.Register(() =>
            {
                Console.WriteLine("Canceled");
            });
            Console.WriteLine("Press enter to cancel task...");
            Console.ReadKey();
            //調用取消
            tokenSource.Cancel();

六、異常處理

異常處理;
對於某些匿名的Task(通過 Task.Run方法生成的,不調用wait,也不關心是否運行完成),某些情況下,記錄它們的異

常錯誤也是有必要的。這些異常稱作未觀察到的異常(unobserved exceptions)。可以通過訂閱一個全局的靜態事件

TaskScheduler.UnobservedTaskException來處理這些異常。只要當一個Task有異常,並且在被垃圾回收的時候,才會觸

發這一個事件。如果Task還處於被引用狀態,或者只要GC不回收這個Task,這個UnobservedTaskException事件就不會被

觸發
GC.Collect();
GC.WaitForPendingFinalizers();

七、Task的狀態


Created:表示默認初始化任務,但是“工廠創建的”實例直接跳過。

WaitingToRun: 這種狀態表示等待任務調度器分配線程給任務執行。

RanToCompletion:任務執行完畢。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM