C# 多線程


轉自https://www.cnblogs.com/yaosj/p/10342883.html

目錄

 


簡介:

Task 對象是一種的中心思想基於任務的異步模式首次引入.NET Framework 4 中。 因為由執行工作Task對象通常以異步方式執行線程池線程上而不是以同步方式在主應用程序線程中,可以使用Status屬性,並將IsCanceled, IsCompleted,和IsFaulted屬性,以確定任務的狀態。

一.Task的創建

1.創建Task類

(1)

1
2
3
4
5
Task task =  new  Task(() =>
{
     Console.WriteLine( "hello world!" );
});
task.Start(); 

(2)

1
2
3
4
new  Task(() =>
{
     Console.WriteLine( "hello world!" );
}).Start();

(3)帶參數

1
2
3
4
new  Task(x =>
{
     Console.WriteLine(x.ToString());
},  "hello world!" ).Start();

(4)帶返回值

1
2
3
4
5
6
Task< string > t =  new  Task< string >(x =>
{
     return  x.ToString();
},  "hello world!" );
t.Start();
Console.WriteLine(t.Result);

  

2.Task.Factory.StartNew

(1)

1
2
3
4
Task.Factory.StartNew(() =>
{
     Console.WriteLine( "hello world!" );
});

(2)帶參數

1
2
3
4
Task.Factory.StartNew(x =>
{
     Console.WriteLine(x.ToString());
},  "hello world!" );

  

(3)帶返回值

1
2
3
4
Task< string > t = Task.Factory.StartNew< string >(() =>
{
     return  "hello world!" ;
});<br>        Console.WriteLine(t.Result);

  

3.Task.Run

1
2
3
4
Task.Run(() =>
{
     Console.WriteLine( "hello world!" );
});

4.TaskStatus

1
2
3
4
5
6
7
8
Created = 0,  //該任務已初始化,但尚未被計划。
WaitingForActivation = 1,  //該任務正在等待 .NET Framework 基礎結構在內部將其激活並進行計划。
WaitingToRun = 2, //該任務已被計划執行,但尚未開始執行。
Running = 3,  //該任務正在運行,但尚未完成。
WaitingForChildrenToComplete = 4, //該任務已完成執行,正在隱式等待附加的子任務完成。
RanToCompletion = 5, //已成功完成執行的任務。
Canceled = 6,  //該任務已通過對其自身的 CancellationToken 引發 OperationCanceledException 對取消進行了確認,此時該標記處於已發送信號狀態;或者在該任務開始執行之前,已向該任務的CancellationToken 發出了信號。 有關詳細信息,請參閱任務取消。
Faulted = 7  //由於未處理異常的原因而完成的任務。

  

二. TaskCreationOptions

 Task.Factory.StartNew和創建Task類可以帶TaskCreationOptions參數而Task.Run不可以帶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//
// 摘要:
//     指定應使用默認行為。
None = 0,
//
// 摘要:
//     提示 System.Threading.Tasks.TaskScheduler 以一種盡可能公平的方式安排任務,這意味着較早安排的任務將更可能較早運行,而較晚安排運行的任務將更可能較晚運行。
PreferFairness = 1,
//
// 摘要:
//     指定任務將是長時間運行的、粗粒度的操作,涉及比細化的系統更少、更大的組件。 它會向 System.Threading.Tasks.TaskScheduler
//     提示,過度訂閱可能是合理的。 可以通過過度訂閱創建比可用硬件線程數更多的線程。 它還將提示任務計划程序:該任務需要附加線程,以使任務不阻塞本地線程池隊列中其他線程或工作項的向前推動。
LongRunning = 2,
//
// 摘要:
//     指定將任務附加到任務層次結構中的某個父級。 默認情況下,子任務(即由外部任務創建的內部任務)將獨立於其父任務執行。 可以使用 System.Threading.Tasks.TaskContinuationOptions.AttachedToParent
//     選項以便將父任務和子任務同步。 請注意,如果使用 System.Threading.Tasks.TaskCreationOptions.DenyChildAttach
//     選項配置父任務,則子任務中的 System.Threading.Tasks.TaskCreationOptions.AttachedToParent 選項不起作用,並且子任務將作為分離的子任務執行。
//     有關詳細信息,請參閱附加和分離的子任務。
AttachedToParent = 4,
//
// 摘要:
//     指定任何嘗試作為附加的子任務執行(即,使用 System.Threading.Tasks.TaskCreationOptions.AttachedToParent
//     選項創建)的子任務都無法附加到父任務,會改成作為分離的子任務執行。 有關詳細信息,請參閱附加和分離的子任務。
DenyChildAttach = 8,
//
// 摘要:
//     防止環境計划程序被視為已創建任務的當前計划程序。 這意味着像 StartNew 或 ContinueWith 創建任務的執行操作將被視為 System.Threading.Tasks.TaskScheduler.Default
//     當前計划程序。
HideScheduler = 16

1. LongRunning

任務是長時間任務,就需要用LongRunning,可能會創建一個非線程池線程來執行該任務,防止阻塞線程池隊列中的其他線程

1
2
3
4
5
6
7
8
9
10
11
12
private  static  void  fun8()
{
     Task.Factory.StartNew(() =>
     {
         Console.WriteLine($ "task1.線程 id {Thread.CurrentThread.ManagedThreadId}. 是否為線程池線程: {Thread.CurrentThread.IsThreadPoolThread}" );
     });
 
     Task.Factory.StartNew(() =>
     {
         Console.WriteLine($ "task2.線程 id {Thread.CurrentThread.ManagedThreadId}. 是否為線程池線程: {Thread.CurrentThread.IsThreadPoolThread}" );
     }, TaskCreationOptions.LongRunning);
}

 運行結果:

 2. 父子任務(AttachedToParent,DenyChildAttach)

AttachedToParent:將子任務附加到父任務上,表現為:附加到父任務上的所有子任務都結束,父任務才結束

DenyChildAttach:不允許子任務附加到父任務上

(1)子任務不附加到父任務

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun5()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Console.WriteLine( "parent" );
 
         Task.Factory.StartNew(() =>
         {
             Thread.Sleep(1000);
             Console.WriteLine( "child" );
         });
     });
     t.ContinueWith(x =>
     {
         Console.WriteLine( "parent over" );
     });
}

 運行結果:

(2)子任務附加到父任務上,使用AttachedToParent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun6()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Console.WriteLine( "parent" );
 
         Task.Factory.StartNew(() =>
         {
             Thread.Sleep(1000);
             Console.WriteLine( "child" );
         },TaskCreationOptions.AttachedToParent);
     });
     t.ContinueWith(x =>
     {
         Console.WriteLine( "parent over" );
     });
}

 運行結果:

(3)拒絕子任務附加到父任務上,使用DenyChildAttach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun7()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Console.WriteLine( "parent" );
 
         Task.Factory.StartNew(() =>
         {
             Thread.Sleep(1000);
             Console.WriteLine( "child" );
         }, TaskCreationOptions.AttachedToParent);
     }, TaskCreationOptions.DenyChildAttach);
     t.ContinueWith(x =>
     {
         Console.WriteLine( "parent over" );
     });
}

  運行結果:

 

 三.CancellationToken 取消任務

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private  static  void  fun4()
{
     CancellationTokenSource cancellationTokenSource =  new  CancellationTokenSource();
     CancellationToken Token = cancellationTokenSource.Token;
     //結束任務回調
     Token.Register(() =>
     {
         Console.WriteLine( "canceled" );
     });
 
     Task.Factory.StartNew(() =>
     {
         try
         {
             while  ( true )
             {
                 Console.WriteLine( "hello world!" );
                 Thread.Sleep(100);
                 Token.ThrowIfCancellationRequested();
             }
         }
         catch  (OperationCanceledException ocex)
         {
         }
         catch  (ObjectDisposedException odex)
         {
         }
         catch  (Exception ex)
         {
         }
 
     }, Token);
 
     Thread.Sleep(1000);
 
     cancellationTokenSource.Cancel();
}

 

執行結果:

當執行cancellationTokenSource.Cancel()后,任務進行到Token.ThrowIfCancellationRequested()代碼后,throw出OperationCanceledException異常,才結束任務並執行cancel回調

 四.方法

Wait 等待 System.Threading.Tasks.Task 完成執行過程
WaitAll  等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程
WaitAny  等待提供的任一 System.Threading.Tasks.Task 對象完成執行過程
WhenAll  創建一個任務,該任務將在所有 System.Threading.Tasks.Task 對象都完成時完成
WhenAny  任何提供的任務已完成時,創建將完成的任務
ContinueWith  創建一個在目標 System.Threading.Tasks.Task 完成時異步執行的延續任務

1 Wait

阻塞當前線程,等待任務執行完成

1
2
3
4
5
6
7
8
9
10
//等待 System.Threading.Tasks.Task 在指定的毫秒數內完成執行。
public  bool  Wait( int  millisecondsTimeout);
//等待 System.Threading.Tasks.Task 完成執行過程。 如果在任務完成之前取消標記已取消,等待將終止。
public  void  Wait(CancellationToken cancellationToken);
//等待 System.Threading.Tasks.Task 完成執行過程。 如果在任務完成之前超時間隔結束或取消標記已取消,等待將終止。      
public  bool  Wait( int  millisecondsTimeout, CancellationToken cancellationToken);      
//等待 System.Threading.Tasks.Task 完成執行過程。
public  void  Wait();
//等待 System.Threading.Tasks.Task 在指定的時間間隔內完成執行。
public  bool  Wait(TimeSpan timeout);

使用方式:

1
2
3
t.Wait(); //無限等待
t.Wait(100); //等待100ms
t.Wait(TimeSpan.FromMilliseconds(1500)); //等待1500ms

例:

1
2
3
4
5
6
7
8
9
10
private  static  void  fun9()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(1000);
         Console.WriteLine( "task" );
     });
     t.Wait();
     Console.WriteLine( "main" );
}

 運行結果:

2.WaitAll 

阻塞當前線程,等待所有任務執行完成

1
2
3
4
5
6
7
8
9
10
//等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程。
public  static  void  WaitAll( params  Task[] tasks);
//等待所有提供的可取消 System.Threading.Tasks.Task 對象在指定的時間間隔內完成執行。
public  static  bool  WaitAll(Task[] tasks, TimeSpan timeout);
//等待所有提供的 System.Threading.Tasks.Task 在指定的毫秒數內完成執行。
public  static  bool  WaitAll(Task[] tasks,  int  millisecondsTimeout);
//等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程(除非取消等待)。
public  static  void  WaitAll(Task[] tasks, CancellationToken cancellationToken);
//等待提供的所有 System.Threading.Tasks.Task 對象在指定的毫秒數內完成執行,或等到取消等待。
public  static  bool  WaitAll(Task[] tasks,  int  millisecondsTimeout, CancellationToken cancellationToken);

  使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private  static  void  fun10()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     //Task.WaitAll(t1, t2);
     //Task.WaitAll(new Task[] { t1, t2 }, 200);
     Task.WaitAll( new  Task[] { t1, t2 }, TimeSpan.FromMilliseconds(200));
     Console.WriteLine( "main" );
}

  

 運行結果:

3.WaitAny

阻塞當前線程,等待任一任務執行完成

1
2
3
4
5
6
7
8
9
10
//等待提供的任一 System.Threading.Tasks.Task 對象完成執行過程。
public  static  int  WaitAny( params  Task[] tasks);
//等待任何提供的 System.Threading.Tasks.Task 對象在指定的時間間隔內完成執行。
public  static  int  WaitAny(Task[] tasks, TimeSpan timeout);
//等待任何提供的 System.Threading.Tasks.Task 對象在指定的毫秒數內完成執行。
public  static  int  WaitAny(Task[] tasks,  int  millisecondsTimeout);
//等待提供的任何 System.Threading.Tasks.Task 對象完成執行過程(除非取消等待)。
public  static  int  WaitAny(Task[] tasks, CancellationToken cancellationToken);
//等待提供的任何 System.Threading.Tasks.Task 對象在指定的毫秒數內完成執行,或等到取消標記取消。
public  static  int  WaitAny(Task[] tasks,  int  millisecondsTimeout, CancellationToken cancellationToken);

 使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private  static  void  fun11()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     //Task.WaitAny(t1, t2);
     //Task.WaitAny(new Task[] { t1, t2 }, 200);
     Task.WaitAny( new  Task[] { t1, t2 }, TimeSpan.FromMilliseconds(200));
     Console.WriteLine( "main" );
}

 結果:

4.WhenAll

不阻塞當前線程,等待所有任務執行完成后,可以進行ContinueWith

1
2
3
4
5
6
7
8
//創建一個任務,該任務將在可枚舉集合中的所有 System.Threading.Tasks.Task 對象都完成時完成。
public  static  Task WhenAll(IEnumerable<Task> tasks);
//創建一個任務,該任務將在數組中的所有 System.Threading.Tasks.Task 對象都完成時完成。
public  static  Task WhenAll( params  Task[] tasks);
//創建一個任務,該任務將在可枚舉集合中的所有 System.Threading.Tasks.Task`1 對象都完成時完成。
public  static  Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks);
//創建一個任務,該任務將在數組中的所有 System.Threading.Tasks.Task`1 對象都完成時完成。
public  static  Task<TResult[]> WhenAll<TResult>( params  Task<TResult>[] tasks);

  使用:

(1)不帶返回值 

1
public  static  Task WhenAll( params  Task[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  void  fun12()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     Task.WhenAll(t1, t2).ContinueWith(t =>
     {
         Console.WriteLine( "WhenAll" );
     });
 
     Console.WriteLine( "main" );
}

  執行結果:

(2)帶返回值

1
public  static  Task<TResult[]> WhenAll<TResult>( params  Task<TResult>[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private  static  void  fun13()
{
     Task< string > t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
         return  "task1" ;
     });
 
     Task< string > t2 = Task.Factory.StartNew< string >(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
         return  "task2" ;
     });
 
     Task.WhenAll( new  Task< string >[] { t1, t2 }).ContinueWith(t =>
     {
         string  s =  "WhenAll:" ;
         foreach  ( string  item  in  t.Result)
         {
             s += item;
         }
         Console.WriteLine(s);
     });
 
     Console.WriteLine( "main" );
}

  執行結果:

5.WhenAny

1
2
3
4
5
6
7
8
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task> WhenAny( params  Task[] tasks);
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task> WhenAny(IEnumerable<Task> tasks);
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task<TResult>> WhenAny<TResult>( params  Task<TResult>[] tasks);
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks);

 使用:

(1)不帶參數

1
public  static  Task<Task> WhenAny( params  Task[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  void  fun14()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     Task.WhenAny(t1, t2).ContinueWith(t =>
     {
         Console.WriteLine( "WhenAny1" );
     });
 
     Console.WriteLine( "main" );
}

  運行結果:

(2)帶參數:

1
public  static  Task<Task<TResult>> WhenAny<TResult>( params  Task<TResult>[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private  static  void  fun15()
         {
             Task< string > t1 = Task.Factory.StartNew(() =>
             {
                 Thread.Sleep(100);
                 Console.WriteLine( "task1" );
                 return  "response task1" ;
             });
 
             Task< string > t2 = Task.Factory.StartNew< string >(() =>
             {
                 Thread.Sleep(150);
                 Console.WriteLine( "task2" );
                 return  "response task2" ;
             });
 
             Task.WhenAny( new  Task< string >[] { t1, t2 }).ContinueWith(t =>
             {
                 t.Result.ContinueWith(tt =>
                 {
                     Console.WriteLine(tt.Result);
                 });
             });
 
             Console.WriteLine( "main" );
         } 

運行結果:

6.ContinueWith

相當於回調

6.1使用

(1)使用lambda表達式方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun1()
{
     Console.WriteLine( "start" );
     Task< string > task1 =  new  Task< string >(() =>
     {
         Console.WriteLine( "task0" );
         return  "task1" ;
     });
     Task< string > task2 = task1.ContinueWith(t =>
     {
         Console.WriteLine(t.Result);
         return  "task2" ;
     });
     task1.Start();
     Console.WriteLine( "end" );
     Console.ReadKey();
}

2.使用函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  void  fun2()
        {
            Console.WriteLine( "start" );
            Task< string > task1 =  new  Task< string >(doTask1);
            Task< string > task2 = task1.ContinueWith(doTask2);
            task1.Start();
            Console.WriteLine( "end" );
            Console.ReadKey();
        }
 
        private  static  string  doTask1()
        {
            Console.WriteLine( "task0" );
            return  "task1" ;
        }
 
        private  static  string  doTask2(Task< string > t)
        {
            Console.WriteLine(t.Result);
            return  "task2" ;
        }

  運行結果:

 6.2 ContineWith和task可能不在同一線程上

例:

1
2
3
4
5
6
7
8
9
10
private  static  void  fun16()
        {
            Task.Factory.StartNew(() =>
             {
                 Console.WriteLine($ "task {Thread.CurrentThread.ManagedThreadId}" );
             }).ContinueWith(t =>
             {
                 Console.WriteLine($ "ContinueWith {Thread.CurrentThread.ManagedThreadId}" );
             });
        }

  運行結果:

 七.TaskFactory類

方法:

StartNew 創建並啟動任務
ContinueWhenAll 創建一個延續任務,該任務在一組指定的任務完成后開始
ContinueWhenAny 創建一個延續 System.Threading.Tasks.Task,它將在提供的組中的任何任務完成后馬上開始
FromAsync 創建一個 System.Threading.Tasks.Task`1,表示符合異步編程模型模式的成對的開始和結束方法

1.ContinueWhenAll

相當於回調

效果其實和WhenAll差不多,只不過ContineWhenAll采用了回調的方式

使用:帶返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private  static  void  fun17()
{
     Task< string > t1 = Task.Factory.StartNew< string >(() =>
     {
         Console.WriteLine( "task1" );
         return  "task1" ;
     });
 
     Task< string > t2 = Task.Factory.StartNew< string >(() =>
     {
         Console.WriteLine( "task2" );
         return  "task2" ;
     });
 
     Task.Factory.ContinueWhenAll( new  Task[] { t1, t2 }, t =>
     {
         string  s =  "ContinueWhenAll:" ;
         foreach  (Task< string > item  in  t)
         {
             s += item.Result;
         }
         Console.WriteLine(s);
     });
}

  運行結果:

 

 2.ContinueWhenAny

相當於回調

效果其實和WhenAny差不多,只不過ContineWhenAny采用了回調的方式

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private  static  void  fun18()
{
     Task< string > t1 = Task.Factory.StartNew< string >(() =>
     {
         Thread.Sleep(10);
         Console.WriteLine( "task1" );
         return  "task1" ;
     });
 
     Task< string > t2 = Task.Factory.StartNew< string >(() =>
     {
         Console.WriteLine( "task2" );
         return  "task2" ;
     });
 
     Task.Factory.ContinueWhenAny( new  Task[] { t1, t2 }, t =>
     {
         Console.WriteLine($ "{(t as Task<string>).Result} 先執行完" );
     });
}

  執行結果:

 

 3.FromAsync

相當於異步委托的精簡寫法,其中ContinueWith相當於異步委托中的callback

使用:

public Task<TResult> FromAsync<TArg1, TResult>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod, TArg1 arg1, object state);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static  void  Main( string [] args)
{
     fun20();
 
     Console.WriteLine( "Main" );
     Console.ReadKey();
}
 
private  static  void  fun20()
{
     var  func =  new  Func< string string >(x =>
     {
         Thread.Sleep(1000);
         Console.WriteLine(x);
         return  "callback" ;
     });
 
     Task.Factory.FromAsync(func.BeginInvoke, func.EndInvoke,  "func" null ).ContinueWith(t =>
     {
         Console.WriteLine(t.Result);
     });
}

  運行結果:

 

 

參考:

https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.task?view=netframework-4.7.2

https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?view=netframework-4.7.2

https://www.cnblogs.com/leo_wl/archive/2012/03/03/2378695.html#_label2


免責聲明!

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



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