原文地址:http://www.cnblogs.com/yinzixin/archive/2011/11/04/2235233.html

.NET 4为了简化多线程编程,提供了System.Threading.Tasks命名空间中的类来帮助开发者进行多线程编程,其中,Task类用来表示一个线程。最简单的Task类接受一个Action委托作为要执行的方法,调用Start方法开始在另一个线程中运行。例如:

using System;
using System.Threading.Tasks;
namespace TaskParallel
{
class Program
{
static void Main(string[] args)
{
Task task=new Task(()=> Console.WriteLine("Write from another thread"));
task.Start();
Console.WriteLine("Main Thread");
Console.ReadLine();
}
}
} 两句输出的顺序是不一定的,但是很有可能是:

Main Thread 
Write from another thread

也可以使用Task.Factory.StartNew方法,这个方法会构造一个Task并且立刻开始运行,相当于将Task的构造函数和Start方法连在一起执行。

Task类还有一个构造函数可以接受Action<object>委托,用来向Action委托传递参数:

static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Task t = new Task(obj => Console.WriteLine("Thread No " + obj), i);
t.Start();
}
Console.ReadLine();
}

输出的结果类似于:

Thread No 2 
Thread No 1 
Thread No 3 
Thread No 0 
Thread No 4

可以使用Task<T>类来获得返回值,T是返回值的类型,例如:

static void Main(string[] args)
{
Task<int> t = new Task<int>(() =>
{
int s=0;
for (int i = 0; i < 10000; i++)
s += i;
return s;
});
t.Start();
Console.WriteLine("I'm computing");
Console.WriteLine(t.Result);
Console.ReadLine();
}
结果为:

I'm computing 
49995000

在访问t.Result的时候,.net 会保证此时Task的代码已经执行完毕,Result已经获得,否则该线程会阻塞,直到Result计算完毕。

Task库提供了一种主动终止线程的方法,先创建一个CancellationTokenSource,将其Token属性通过Task构造函数传进去,在Task内部轮询token的IsCancellationReqeusted属性,如果检测到为true,则主动终止线程。在父线程内调用tokenSource的Cancel方法,可以终止线程。注意,这是线程主动终止自己的方法,必须在Task内的代码自己终止,.NET不会强行终止task线程,即使父线程调用了tokenSource的Cancel方法。

例如下面的代码,如果在else语句块内没有break语句,子线程是不会终止的。

using System;
using System.Threading;
using System.Threading.Tasks; namespace TaskParallel
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tks = new CancellationTokenSource();
CancellationToken token = tks.Token;
long i = 0;
Task task = new Task(() =>
{
while (true)
{
if (!token.IsCancellationRequested)
i++;
else
{
Console.WriteLine("Task is canceled, it looped "+i+" times");
break;
}
}
},token);
task.Start();
Console.WriteLine("Press Enter to Cancel task");
Console.ReadLine();
tks.Cancel();
Console.ReadLine();
}
}
}

还可以通过CancellationToken的Register方法给token注册一个委托,当调用tokenSource的Cancel方法的时候,这个委托会被执行。这个委托是在调用Cancel方法的线程中执行的。

token.Register(() => { Console.WriteLine("Delegate Invoked"); });
 
要挂起当前线程,等待一个线程执行完成,可以使用执行线程的Wait()方法,Wait方法有一些重载方法,可以指定等待的时间等,例如:
static void Main(string[] args)
{
Task t1 = new Task(() =>
{
Console.WriteLine("Task 1 Starts...");
Thread.Sleep(3000);
Console.WriteLine("Task1 Ends");
});
t1.Start();
Task t2 = new Task(() =>
{
Console.WriteLine("Task2 Starts...");
t1.Wait();
Console.WriteLine("Task2 Ends");
});
Task t3 = new Task(() =>
{
Console.WriteLine("Task 3 is waiting Task1 for 1 second...");
bool finished=t1.Wait(1000);
if (finished)
Console.WriteLine("T1 is finished");
else
Console.WriteLine("T1 is not finished");
});
t2.Start();
t3.Start();
Console.ReadLine();
}

执行结果为:

Task 1 Starts... 
Task2 Starts... 
Task 3 is waiting Task1 for 1 second... 
T1 is not finished 
Task1 Ends 
Task2 Ends

要等到多个线程都执行完可以使用Task.WaitAll方法,

Task t4 = new Task(() =>
{
Console.WriteLine("Waiting for all");
Task.WaitAll(t1, t2, t3);
Console.WriteLine("Task4 Ends");
});
t4.Start();

还有一个Task.WaitAny,可以等待一组线程中的任何一个方法执行完毕,用法类似。

下面介绍Task中的异常处理,通常情况下,线程委托中的异常会导致线程终止,但异常并不会被抛出,例如:

Task t = new Task(() =>
{
throw new Exception();
Console.WriteLine("Thread Ends");
});
t.Start();
Console.WriteLine("Main Ends");

输出的是:

Main Ends

当调用Wait,WaitAll,WaitAny,Task.Result的时候,会抛出AggerateException ,在AggerateExcepiton中可以处理所有线程抛出的异常:

static void Main(string[] args)
{
Task t1 = new Task(() =>
{
throw new Exception();
Console.WriteLine("T1 Ends");
}); Task t2 = new Task(() =>
{
throw new ArgumentException();
Console.WriteLine("T2 Ends");
});
t1.Start();
t2.Start();
try
{
Task.WaitAll(t1, t2);
}
catch (AggregateException ex)
{
foreach (var inner in ex.InnerExceptions)
{
Console.WriteLine(inner.GetType() + " " + inner.Source);
}
}
Console.WriteLine("Main Ends");
}
}

有时候需要区分对待某些异常,一些异常直接处理掉,一些异常需要再次抛出,AggregateException提供一个Handle方法,接收一个Func<Exception,bool>委托作为参数,如果不需要再次抛出则返回true,否则返回false。

try
{
Task.WaitAll(t1, t2);
}
catch (AggregateException ex)
{
foreach (var inner in ex.InnerExceptions)
{
Console.WriteLine(inner.GetType() + " " + inner.Source);
}
ex.Handle((e) =>
{
if (e is ArgumentException)
{
Console.WriteLine("Argument Exception is captured");
return true;
}
else
return false;
});
}
这样就会有未处理的异常被抛出,抛出的仍然是AggregateException.

.NET 4中的多线程编程之一:使用Task(转载)的更多相关文章

  1. JavaEE开发之Spring中的多线程编程以及任务定时器详解

    上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于<JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换&g ...

  2. Java中的 多线程编程

    Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...

  3. Python中的多线程编程,线程安全与锁(二)

    在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...

  4. Qt中的多线程编程

    http://www.ibm.com/developerworks/cn/linux/l-qt-mthrd/ Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功 ...

  5. C语言中的多线程编程

    很久很久以前,我对C语言的了解并不是很多,我最早听说多线程编程是用Java,其实C语言也有多线程编程,而且更为简单.方便.强大.下面就让我们简单领略一下Unix C语言环境下的多线程编程吧! 下面先看 ...

  6. 每天进步一点点——论fork()函数与Linux中的多线程编程

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/27316803 一.fork()函数     在操作系统的基本概念中进程是程序的一次运行,且是 ...

  7. Python中的多线程编程,线程安全与锁(一)

    1. 多线程编程与线程安全相关重要概念 在我的上篇博文 聊聊Python中的GIL 中,我们熟悉了几个特别重要的概念:GIL,线程,进程, 线程安全,原子操作. 以下是简单回顾,详细介绍请直接看聊聊P ...

  8. Android中的多线程编程(一)附源代码

    Android中多线程编程:Handler类.Runnable类.Thread类之概念分析 1.Handler类: Handler是谷歌封装的一种机制:能够用来更新UI以及消息的发送和处理.Handl ...

  9. python中的多线程编程与暂停、播放音频的结合

    先给两个原文链接: https://blog.csdn.net/u013755307/article/details/19913655 https://www.cnblogs.com/scolia/p ...

随机推荐

  1. Js 过滤emoji表情...持续补充中..

    原文来自: https://www.cnblogs.com/tsjTSJ/p/7065544.html 最全最详细的用JS过滤Emoji表情的输入   在前端页面开发过程中,总会碰到不允许输入框输入e ...

  2. intellij idea 的安装与简单使用

    1.将安装包拷贝到指定目录,特别注意不要有中文路径和空格,路径不要太深 2.点击安装(如果是win10系统要使用管理员权限安装)       3. 4.修改默认安装目录:一般来说我们都不要把软件安装在 ...

  3. Css定位元素

    Css定位selenium极力推荐使用Css定位,而不是xpath定位元素,原因是css定位比xpath定位块,速度快,语法更加简洁 css常用的定位方法:1.find_element_by_css_ ...

  4. Redis网络协议

    Redis网络协议较为简单,易于阅读. 命令或数据已\r\n结尾,但除了状态回复,其他数据都是二进制安全的(包含长度) 头部如下: + 正确的状态信息,具体信息是当前行+后面的字符. -  一条错误信 ...

  5. Swift4 - 动态计算UITableView中tableHeaderView的高度 - 获取子控件高度和宽度

    核心 : /// 获取 子控件高度 func sizeHeaderToFit(view:UIView) { view.setNeedsLayout() view.layoutIfNeeded() le ...

  6. dede数据库内容替换,去掉文章内容中的img标签

    1.织梦已经给我们准备好了数据库内容替换工具,在采集->批量维护->数据库内容替换 2.织梦的文章内容一般在放在dede_addonarticle表body字段中. (1).选择好数据表和 ...

  7. geoserver的rest服务介绍,搭建java程序

    在geoserver中使用 Restlet 来提供所有的rest服务,并且geoserver中所有的在/rest目录下的请求都被看作为一个restful server,下图就是rest服务的调用过程 ...

  8. windows驱动

    DriveEntry() 启动 停止 接口函数 驱动程序名 驱动程序版本号 异常处理 是否运行 声明LPOReceive类型的函数 声明函数指针(外部传进来的回调函数) 存放配置字符串 本机IP 串口 ...

  9. Ubuntu 网卡多个 IP 地址

    临时添加 IP 地址 首先,让我们找到网卡的 IP 地址.在我的 Ubuntu 15.10 服务器版中,我只使用了一个网卡. 运行下面的命令找到 IP 地址: 复制代码 代码如下: sudo ip a ...

  10. css之颜色篇

      app多采用浅灰#f5f5f5   白色一般用white,如果觉得白太直接了,可以加一点点灰,#fefefe,   这种情况下搭配#e4e4e4的浅灰边框最合适.