c#多线程之Task

上一篇文章写了通过QueueUserWorkItem进行异步操作,但是通过ThreadPool的QueueUserWorkItem进行异步操作有一个缺点,我们无法知道异步操作何时结束,并且无法获取返回值,这些都可以利用Task来获得解决。

首先,还是来一段示例代码

Task<Int32> task = new Task<Int32>(n=>TaskTest2(10),0);
            task.Start();
int a=task.Result;
private int TaskTest2(int num)
        {
            for (int i = 0; i < int.MaxValue - 10; i++)
            {
                num++;
            }
            return num;
        }

上面的示例代码新建了一个Task对象,并调用了TaskTest2方法,此处的的操作相当于ThreadPool.QueueUserWorkItem(TaskTest2);但不同的是,这次有返回值,我们可以通过Task的Result属性获取到此次异步操作的结果,当我们调用Result的时候,其实会隐式的调用Task的wait()函数,于是

int a=task.Result实际上相当于task.wait();int a=task.Result;

此处必须注意的是,当我们新建一个Task的时候,其实是相当于启用一个新的线程来进行计算的,但是当我们调用wait()函数的时候,调用wait()的线程,也就是当前的线程会检测调用的方法是否已经结束,如果调用的TastTest2尚未执行结束的话,当前线程会选择等待,知道调用的方法在新线程中执行完毕,因此,如果当函数尚未执行完毕时,我们调用task.Result,这时我们会发现当前线程阻塞了,这很明显是我们不想看到的。

于是我们改写一下获取Result的方式,如下

Task tsk = task.ContinueWith(t => {int a = task.Result});
此处我们调用了Task的ContinueWith方法,指示Task在耗时操作执行完毕时新建一个新的Task执行回调操作,这时,调用Result时我们可以确定耗时操作已经执行完毕,因此线程不会阻塞。

接下来介绍一下通过任务启动子任务,实例如下

扫描二维码关注公众号,回复: 1737264 查看本文章
 Task<Int32[]> tasks = new Task<Int32[]>(() =>
            {
                Int32[] results = new Int32[3];
                Task<int> child1 = new Task<int>(n => TaskTest2(1), TaskCreationOptions.AttachedToParent);
                Task<int> child2 = new Task<int>(n => TaskTest2(2), TaskCreationOptions.AttachedToParent);
                Task<int> child3 = new Task<int>(n => TaskTest2(3), TaskCreationOptions.AttachedToParent);
                child1.Start();
                child2.Start();
                child3.Start();
                results[0] = child1.Result;
                results[1] = child2.Result;
                results[2] = child3.Result;
                return results;
            });
此处我们新建了一个返回int数组的父任务,他包含了三个子任务,子任务通过TaskCreationOptions.AttachedToParent将其自身表示为子任务,他有个特点,在于,只有它的所有的子任务都完成时,整个任务才算完全结束。此时调用ContinueWith我们会获得一个包含三个数字的数组。
下面再给出一个利用任务工厂新建的带子任务的父任务

Task<int[]> parent = new Task<int[]>(() => 
            {
                Int32[] results = new Int32[3];
                var cts = new CancellationTokenSource();
                var factory = new TaskFactory<int>(cts.Token,TaskCreationOptions.AttachedToParent,TaskContinuationOptions.ExecuteSynchronously,TaskScheduler.Default);
                var childrenTasks=new []{
                factory.StartNew(() => TaskTest(1, cts.Token)),
                factory.StartNew(() => TaskTest(2, cts.Token)),
                factory.StartNew(() => TaskTest(2, cts.Token))
                };
                for (int tasknum = 0; tasknum < childrenTasks.Length; tasknum++)
                {
                    results[tasknum] = childrenTasks[tasknum].Result;
                }
                return results;
            });
不同自出在于,这个方法通过任务工厂新建,任务工厂主要用于批量创建具有相同属性的任务,如它的子任务共用CancellationToken等等,上面便新建了个一个共用CancellationToken,并且子任务间同步执行而非异步执行的异步操作,其实说到底就是一个批量生产任务的过程。

以上这两个父任务的调用方法和普通的任务没有任何却别,同样是调用Task的Result属性便可得到返回值,此处不再赘述

猜你喜欢

转载自blog.csdn.net/xiaomingelv/article/details/42275555