C#多线程操作(二):异步任务

1、从Thread到Task

     创建线程的代价高昂,而且每个线程都要占用大量虚拟内存(例如Windows默认1MB)。在.Net Framework 4及后续版本中,异步操作不是每次创建一个线程,而是创建一个Task,并告诉任务调度器有异步工作要执行。此时调度器可能采取多种策略,但默认是从线程池请求一个工作线程,线程池会自行判断怎么做最高效。

    任务是对象,其中封装了以异步方式执行的工作。Task与委托的区别:委托是同步的,而任务是异步的。执行委托的时候,当前线程的控制点会立即转移到委托的代码。除非委托结束,否则控制不会返回调用者。而启动任务,控制几乎立即返回调用者,无论任务要执行多少工作。总而言之:任务将委托从同步执行模式转变为异步。

代码示例如下:

 #region 3、Task异步任务
        public static void Main1()
        {
            Task task = Task.Run(() =>
             {
                 for (int count = 0; count < _Repetitions; count++)
                 {
                     Console.Write('-');
                 }
             }
            );
            Task task2 = Task.Run(() =>
            {
                for (int count = 0; count < _Repetitions; count++)
                {
                    Console.Write('q');
                }
            }
           );
            //直到task任务完成
            //调用Wait强迫主线程等待分配给任务的所有工作完成,相当于调用线程的Join()操作
            //task.Wait();
            //等待一组任务执行完成,当然,目前只有一个任务
            // Task.WaitAll(task,task2);
            //等待其中一个执行完成
            Task.WaitAny(task, task2);
            for (int count = 0; count < _Repetitions; count++)
            {
                Console.Write('+');
            }
            Console.ReadKey();
            // task.Wait();
        }

        #endregion

#region 任务延续 
        public static void Main()
        {
            Console.WriteLine("Before");
            Task taskA = Task.Run(() =>
                 Console.WriteLine("Starting...")
            ).ContinueWith(antecedent=>Console.WriteLine("Continue A...."));
            Task taskB = taskA.ContinueWith(antecedent => Console.WriteLine("Continuing B....."));
            Task taskC = taskA.ContinueWith(antecedent => Console.WriteLine("Contiuing C..."));
            Task.WaitAll(taskB,taskC);
            Console.WriteLine("Finished!");
            Console.ReadKey();
        }
        #endregion

2、Task上异常处理

在同步调用的场景下,异常处理可以直接包裹在try代码块中,用catch子句告诉编译器发生异常时应执行什么代码,但在异步的场景下不能这么做,不能用try包装Start()调用来捕捉异常,因为控制会立即从调用返回,然后控制会离开try块。一个解决方案是将任务的委托主体包装到try/catch代码块中。

 #region 异常处理
        public static void Main_1()
        {
            Task task = Task.Run(() =>
             {
                 throw new InvalidOperationException();
             });
            try
            {
                task.Wait();
            }
            catch (AggregateException exception)
            {
                exception.Handle(eachException =>
                {
                    Console.WriteLine($"开始打印ERROR:{eachException.Message}");
                    return true; 
                });
            }
        }

        public static void Main_2()
        {
            bool parentTaskFaulted = false; 
            Task task = Task.Run(() =>
            {
                throw new InvalidOperationException();
            });
            Task continuationTask = task.ContinueWith((antecedentTask) => {
                parentTaskFaulted = antecedentTask.IsFaulted;
            },TaskContinuationOptions.OnlyOnFaulted);
            task.Start();
            continuationTask.Wait();
            Trace.Assert(parentTaskFaulted);
            Trace.Assert(task.IsFaulted);
            task.Exception.Handle(eachException =>
                {
                    Console.WriteLine($"开始打印ERROR:{eachException.Message}");
                    return true;
                });
            }

        //处理Thread上的未处理异常
        public static Stopwatch clock = new Stopwatch();
        public static void Main()
        {
            try
            {
                clock.Start();
                AppDomain.CurrentDomain.UnhandledException += (s, e) =>
                  {
                      Message("Event handler starting");
                      Delay(4000);
                  };
                Thread thread = new Thread(() =>
                 {
                     Message("Throwing exception ");
                     throw new Exception();
                 });
                thread.Start();
                Delay(2000);
            }
            finally
            {
                Message("Finally block running.");
            }
        }
        static void Delay(int i)
        {
            Message($"Sleeping for {i} ms");
            Thread.Sleep(i);
            Message("Awwake");
        }
        static void Message(string text)
        {
            Console.WriteLine("{0}:{1:0000}:{2}",
            Thread.CurrentThread.ManagedThreadId, clock.ElapsedMilliseconds,text);
        }

        #endregion

执行结果如下:

当然在实际代码中需要注意一下3点:

1、避免程序在任何线程上产生未处理异常

2、考虑登记“未处理异常”事件处理程序以进行调试、记录和紧急关闭。

3、要取消未完成的任务而不要在程序关闭期间允许其运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值