C#当中的多线程_任务并行库(中)

发现自己有点懒了!也可能是越往后越难了,看书理解起来有点费劲,所以这两天就每天更新一点学习笔记吧。

4.5 APM模式转化为任务

 书上提供的三种方式

 方式一:

1  class Program
2         {
3                 //定义一个委托
4                 private delegate string AsynchronousTask(string threadName);
5 
6                 static void Main(string[] args)
7                 {
8                         //实例化一个委托对象,绑定Test函数
9                         AsynchronousTask d = Test;
10 
11                       Console.WriteLine("Option 1");
12                       //调用TaskFactory<TResult> Factory.FromAsync()方法,这个方法有很多重载函数
13                       //这个方法是 public Task<TResult> FromAsync(IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod);
14                       Task<string> task = Task<string>.Factory.FromAsync(
15                       d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);
16                       //绑定任务执行完的后续操作
17                       task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
18                                 t.Result));
19 
20                         //循环打印状态信息
21                         while (!task.IsCompleted)
22                         {
23                                 Console.WriteLine(task.Status);
24                                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
25                         }
26                         Console.WriteLine(task.Status);
27                         Thread.Sleep(TimeSpan.FromSeconds(1));
28 
29                         Console.WriteLine("----------------------------------------------");
30                         Console.WriteLine();
31                 }
32 
33                 //定义一个回调函数
34                 private static void Callback(IAsyncResult ar)
35                 {
36                         Console.WriteLine("Starting a callback...");
37                         Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
38                         Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
39                         Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
40                 }
41 
42                 //定义一个委托函数
43                 private static string Test(string threadName)
44                 {
45                         Console.WriteLine("Starting...");
46                         Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
47                         Thread.Sleep(TimeSpan.FromSeconds(2));
48                         Thread.CurrentThread.Name = threadName;
49                         return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
50                 }

方式二:

与方式一差不多,但是使用了TaskFactory<TResult> Factory.FromAsync()方法的另一种重载,该重载并不允许指定一个将会在异步委托调用后被调用的回调函数。但是可以使用后续操作替代它。如果回调函数非常重要,建议使用第一种。

 

1  class Program
2         {
3                 //定义一个委托
4                 private delegate string AsynchronousTask(string threadName);
5 
6                 static void Main(string[] args)
7                 {
8                          //实例化一个委托对象,绑定Test函数
9                          AsynchronousTask d = Test;
10 
11                        Console.WriteLine("Option 2");
12                        //调用TaskFactory<TResult> Factory.FromAsync()方法,这个方法有很多重载函数
13                        /*
14                         * 这个方法重载是 
15                         * public Task<TResult> FromAsync<TArg1>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, 
16                         * Func<IAsyncResult, TResult> endMethod, 
17                         * TArg1 arg1, 
18                         * object state);
19                         */
20                       Task<string> task= Task<string>.Factory.FromAsync(d.BeginInvoke,d.EndInvoke,
21                                                                                                     "AsyncTaskThread", 
22                                                                                                     "a delegate asynchronous call");
23                                 
24                         //绑定任务执行完的后续操作
25                       task.ContinueWith(t => Console.WriteLine("Task is completed, now running a continuation! Result: {0}",
26                               t.Result));
27 
28                         //循环打印状态信息
29                         while (!task.IsCompleted)
30                         {
31                                 Console.WriteLine(task.Status);
32                                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
33                         }
34                         Console.WriteLine(task.Status);
35                         Thread.Sleep(TimeSpan.FromSeconds(1));
36 
37                         Console.WriteLine("----------------------------------------------");
38                         Console.WriteLine();
39                 }
40 
41                 //定义一个委托函数
42                 private static string Test(string threadName)
43                 {
44                         Console.WriteLine("Starting...");
45                         Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
46                         Thread.Sleep(TimeSpan.FromSeconds(2));
47                         Thread.CurrentThread.Name = threadName;
48                         return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
49                 }

方式三:

1  class Program
2         {
3                 private delegate string IncompatibleAsynchronousTask(out int threadId);
4 
5                 static void Main(string[] args)
6                 {
7                         int threadId;
8                         IncompatibleAsynchronousTask e = Test;
9 
10                         Console.WriteLine("Option 3");
11                         
12                         IAsyncResult ar = e.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");
13 
14                         /*这是一个小技巧,EndMethod使用了out参数,与FromAsync的方法重载并不兼容。
15                          * 然而,可以很轻松地将EndMethod调用封装到一个lambda表达式当中,从而适合
16                          * 工厂方法。
17                          */
18                         Task<string> task = Task<string>.Factory.FromAsync(ar, _ => e.EndInvoke(out threadId, ar));
19                         task.ContinueWith(t =>
20                                 Console.WriteLine("Task is completed, now running a continuation! Result: {0}, ThreadId: {1}",
21                                         t.Result, threadId));
22 
23                         while (!task.IsCompleted)
24                         {
25                                 Console.WriteLine(task.Status);
26                                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
27                         }
28                         Console.WriteLine(task.Status);
29 
30                         Thread.Sleep(TimeSpan.FromSeconds(1));
31                 }
32 
33                 private static void Callback(IAsyncResult ar)
34                 {
35                         Console.WriteLine("Starting a callback...");
36                         Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
37                         Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
38                         Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
39                 }
40 
41                 private static string Test(out int threadId)
42                 {
43                         Console.WriteLine("Starting...");
44                         Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
45                         Thread.Sleep(TimeSpan.FromSeconds(2));
46                         threadId = Thread.CurrentThread.ManagedThreadId;
47                         return string.Format("Thread pool worker thread id was: {0}", threadId);
48                 }
49  

总结:感觉这个在日常工作当中使用的真的不是很多,比较晦涩难懂,暂且记住有TaskFactory<TResult> Factory.FromAsync()这个方法,通过这个方法可以将APM转化成TPL

4.6 EAP模式转换成任务

例子先上:

1 class Program
2     {
3         static void Main(string[] args)
4         {
5             //实例化一个TaskCompletionSource<TResult>,它是实现EAP转化成TPL的关键
6             var tcs = new TaskCompletionSource<int>();
7 
8             var worker = new BackgroundWorker();
9             worker.DoWork += (sender, eventArgs) =>
10             {
11                 eventArgs.Result = TaskMethod("Background worker", 5);
12             };
13 
14             worker.RunWorkerCompleted += (sender, eventArgs) =>
15             {
16                  //如果有错就抛出异常
17                 if (eventArgs.Error != null)
18                 {
19                     tcs.SetException(eventArgs.Error);
20                 }
21                  //如果是取消操作,就取消操作
22                 else if (eventArgs.Cancelled)
23                 {
24                     tcs.SetCanceled();
25                 }
26                 else
27                 {
28                     //正常情况返回结果
29                     tcs.SetResult((int)eventArgs.Result);
30                 }
31             };
32 
33             //运行任务
34             worker.RunWorkerAsync();
35 
36             //获取结果
37             int result = tcs.Task.Result;
38 
39             Console.WriteLine("Result is: {0}", result);
40         }
41 
42         static int TaskMethod(string name, int seconds)
43         {
44             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
45                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
46             Thread.Sleep(TimeSpan.FromSeconds(seconds));
47             return 42 * seconds;
48         }

4.7 实现取消选项

我们在前面说过线程工作的取消需要依靠两个类来实现,分别是CancellationTokenSourceCancellationToken这两个类

1     class Program
2     {
3         private static void Main(string[] args)
4         {
5               //定义一个CancellationTokenSource类
6               var cts = new CancellationTokenSource();
7               //创建第一个任务,这里有个很奇怪的第地方,cts.Token被传了两次
8               //分别传给了TaskMethod方法个Task的构造函数,为什么这么做呢?
9               var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
10             //打印任务状态
11             Console.WriteLine(longTask.Status);
12             //取消任务
13             cts.Cancel();
14             //再次打印任务状态
15             Console.WriteLine(longTask.Status);
16             Console.WriteLine("First task has been cancelled before execution");
17 
18             //创建第二个任务
19             cts = new CancellationTokenSource();
20             longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
21             //启动任务
22             longTask.Start();
23             for (int i = 0; i < 5; i++ )
24             {
25                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
26                 Console.WriteLine(longTask.Status);
27             }
28             //取消任务
29             cts.Cancel();
30             //打印任务状态
31             for (int i = 0; i < 5; i++)
32             {
33                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
34                 Console.WriteLine(longTask.Status);
35             }
36 
37             Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
38         }
39 
40         private static int TaskMethod(string name, int seconds, CancellationToken token)
41         {
42             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
43                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
44             for (int i = 0; i < seconds; i ++)
45             {
46                 Thread.Sleep(TimeSpan.FromSeconds(1));
47                 //如果任务被取消,就返回-1
48                 if (token.IsCancellationRequested) return -1;
49             }
50             return 42*seconds;
51          }
52    }

cts.Token被传了两次为什么呢?如果在任务实际启动前取消它,该任务的TPL基础设施有责任处理该取消操作,因为这些代码根本不会被执行,通过得到第一个任务的状态可以知道它被取消了。如果尝试对该任务调用Start方法,将会得到InvalidOperationException异常。

 

解释:

如果在Task构造函数当中取消了,cts.Token这个参数,那么在Cts.Cancel()后面执行longTask.Start(); 会出现什么情况呢?

如下图所示,任务只有在运行操作的时候才能检查到取消操作。所以才会有WaitingToRun这个状态出现。

如果添加了这个参数,结果如下:

这个时候,在取消操作执行完后,执行开始操作就会抛出异常。

原文地址:https://www.cnblogs.com/dcz2015/p/5057601.html