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