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

时间:2023-03-09 15:41:08
C#当中的多线程_任务并行库(中)

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

4.5 将APM模式转化为任务

书上提供的三种方式

方式一:

  class Program
        {
                //定义一个委托
                private delegate string AsynchronousTask(string threadName);                 static void Main(string[] args)
                {
                        //实例化一个委托对象,绑定Test函数
                        AsynchronousTask d = Test;                       Console.WriteLine("Option 1");
                      //调用TaskFactory<TResult> Factory.FromAsync()方法,这个方法有很多重载函数
                      //这个方法是 public Task<TResult> FromAsync(IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod);
                      Task<string> task = Task<string>.Factory.FromAsync(
                      d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);
                      //绑定任务执行完的后续操作
                      task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
                                t.Result));                         //循环打印状态信息
                        while (!task.IsCompleted)
                        {
                                Console.WriteLine(task.Status);
                                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                        }
                        Console.WriteLine(task.Status);
                        Thread.Sleep(TimeSpan.FromSeconds());                         Console.WriteLine("----------------------------------------------");
                        Console.WriteLine();
                }                 //定义一个回调函数
                private static void Callback(IAsyncResult ar)
                {
                        Console.WriteLine("Starting a callback...");
                        Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
                        Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                        Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
                }                 //定义一个委托函数
                private static string Test(string threadName)
                {
                        Console.WriteLine("Starting...");
                        Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                        Thread.Sleep(TimeSpan.FromSeconds());
                        Thread.CurrentThread.Name = threadName;
                        return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
                }

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

方式二:

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

  class Program
        {
                //定义一个委托
                private delegate string AsynchronousTask(string threadName);                 static void Main(string[] args)
                {
                        //实例化一个委托对象,绑定Test函数
                        AsynchronousTask d = Test;                        Console.WriteLine("Option 2");
                       //调用TaskFactory<TResult> Factory.FromAsync()方法,这个方法有很多重载函数
                       /*
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 */
                      Task<string> task= Task<string>.Factory.FromAsync(d.BeginInvoke,d.EndInvoke,
                                                    "AsyncTaskThread",
                                             "a delegate asynchronous call");
                                
                        //绑定任务执行完的后续操作
                      task.ContinueWith(t => Console.WriteLine("Task is completed, now running a continuation! Result: {0}",
                              t.Result));                         //循环打印状态信息
                        while (!task.IsCompleted)
                        {
                                Console.WriteLine(task.Status);
                                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                        }
                        Console.WriteLine(task.Status);
                        Thread.Sleep(TimeSpan.FromSeconds());                         Console.WriteLine("----------------------------------------------");
                        Console.WriteLine();
                }                 //定义一个委托函数
                private static string Test(string threadName)
                {
                        Console.WriteLine("Starting...");
                        Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                        Thread.Sleep(TimeSpan.FromSeconds());
                        Thread.CurrentThread.Name = threadName;
                        return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
                }

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

方式三:

  class Program
        {
                private delegate string IncompatibleAsynchronousTask(out int threadId);                 static void Main(string[] args)
                {
                        int threadId;
                        IncompatibleAsynchronousTask e = Test;                         Console.WriteLine("Option 3");
                        
                        IAsyncResult ar = e.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");                         /*这是一个小技巧,EndMethod使用了out参数,与FromAsync的方法重载并不兼容。
15 * 然而,可以很轻松地将EndMethod调用封装到一个lambda表达式当中,从而适合
16 * 工厂方法。
17 */
                        Task<string> task = Task<string>.Factory.FromAsync(ar, _ => e.EndInvoke(out threadId, ar));
                        task.ContinueWith(t =>
                                Console.WriteLine("Task is completed, now running a continuation! Result: {0}, ThreadId: {1}",
                                        t.Result, threadId));                         while (!task.IsCompleted)
                        {
                                Console.WriteLine(task.Status);
                                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                        }
                        Console.WriteLine(task.Status);                         Thread.Sleep(TimeSpan.FromSeconds());
                }                 private static void Callback(IAsyncResult ar)
                {
                        Console.WriteLine("Starting a callback...");
                        Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
                        Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                        Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
                }                 private static string Test(out int threadId)
                {
                        Console.WriteLine("Starting...");
                        Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                        Thread.Sleep(TimeSpan.FromSeconds());
                        threadId = Thread.CurrentThread.ManagedThreadId;
                        return string.Format("Thread pool worker thread id was: {0}", threadId);
                }
 

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

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

4.6 将EAP模式转换成任务

例子先上:

 class Program
    {
        static void Main(string[] args)
        {
            //实例化一个TaskCompletionSource<TResult>,它是实现EAP转化成TPL的关键
            var tcs = new TaskCompletionSource<int>();             var worker = new BackgroundWorker();
            worker.DoWork += (sender, eventArgs) =>
            {
                eventArgs.Result = TaskMethod("Background worker", );
            };             worker.RunWorkerCompleted += (sender, eventArgs) =>
            {
                 //如果有错就抛出异常
                if (eventArgs.Error != null)
                {
                    tcs.SetException(eventArgs.Error);
                }
                 //如果是取消操作,就取消操作
                else if (eventArgs.Cancelled)
                {
                    tcs.SetCanceled();
                }
                else
                {
                    //正常情况返回结果
                    tcs.SetResult((int)eventArgs.Result);
                }
            };             //运行任务
            worker.RunWorkerAsync();             //获取结果
            int result = tcs.Task.Result;             Console.WriteLine("Result is: {0}", result);
        }         static int TaskMethod(string name, int seconds)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            return * seconds;
        }

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

4.7 实现取消选项

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

     class Program
    {
        private static void Main(string[] args)
        {
              //定义一个CancellationTokenSource类
             var cts = new CancellationTokenSource();
              //创建第一个任务,这里有个很奇怪的第地方,cts.Token被传了两次
              //分别传给了TaskMethod方法个Task的构造函数,为什么这么做呢?
             var longTask = new Task<int>(() => TaskMethod("Task 1", , cts.Token), cts.Token);
            //打印任务状态
            Console.WriteLine(longTask.Status);
            //取消任务
            cts.Cancel();
            //再次打印任务状态
            Console.WriteLine(longTask.Status);
            Console.WriteLine("First task has been cancelled before execution");             //创建第二个任务
            cts = new CancellationTokenSource();
            longTask = new Task<int>(() => TaskMethod("Task 2", , cts.Token), cts.Token);
            //启动任务
            longTask.Start();
            for (int i = ; i < ; i++ )
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine(longTask.Status);
            }
            //取消任务
            cts.Cancel();
            //打印任务状态
            for (int i = ; i < ; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine(longTask.Status);
            }             Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
        }         private static int TaskMethod(string name, int seconds, CancellationToken token)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            for (int i = ; i < seconds; i ++)
            {
                Thread.Sleep(TimeSpan.FromSeconds());
                //如果任务被取消,就返回-1
                if (token.IsCancellationRequested) return -;
            }
            return *seconds;
     }
}

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

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

解释:

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

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

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

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

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

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