C#并行库(TaskParallelLibrary)用法小结
今天有空,总结一下.NET 4.5并行库(TaskParallelLibrary)用法。

也许C和C++的程序员刚刚开始写C#还习惯于new Thread来新建一个线程,但新建线程需要内存和CPU上下文切换的开销,200,000个周期,销毁线程也需要100,000个周期;所以还需要实现一个线程池Threadpool。自从有了并行库(TaskParallelLibrary),这些都不需要了。使用Task.Factory.StartNew(() => DoSomething(item));可以创建一个线程并自动由线程池管理。写法非常简单,但其实里面误区很多:
1. Task.Factory.StartNew(() => DoSomeWork())不是阻塞的
下面的代码会先输出ddd,因为Task.Factory.Startnew不阻塞:
var task = Task.Factory.StartNew(() => Console.WriteLine("eee"));
Console.WriteLine("ddd");
如果你想阻塞,应该加上wait,改为这样:
var task = Task.Factory.StartNew(() => Console.WriteLine("eee")).Wait();
Console.WriteLine("ddd");
同样,Task.Factory.StartNew(() => DoSomeWork()).ContinueWith…也是是异步的,想让它阻塞,应该加上wait,这样写:
var task = Task.Factory.StartNew(() => return "").ContinueWith( s => { Console.WriteLine(s.Result); }).Wait();
Console.WriteLine("ddd");
2. Task.Factory.StartNew(() => DoSomeWork()).ContinueWith…没有运行在新的线程里
var task = Task.Factory.StartNew(() => return "").ContinueWith( s =>
{
DoSomething2(s.Result);
}).Wait();
Console.WriteLine("ddd");
注意上面的DoSomething2()是运行在主线程,而不是在新的线程里
3. Parallel.ForEach为何导致内存溢出
如果对一个10000个item的collection使用Parallel.ForEach,可以想象会发生什么。TPL默认是Parallel.ForEach使用场景是对CPU敏感的,TPL会持续创建线程,直到你的CPU利用率达100%;问题是你的使用场景如果不是CPU敏感的,例如是I/O敏感的,TPL想尽可能的利用你的CPU,所以检测你的CPU利用率,如果还不是100%就会一直创建线程....直到内存耗尽。所以,使用要注意使用场景十分CPU敏感的,另外可以加一个参数来限制TPL线程的创建:
Parallel.ForEach(items,
new ParallelOptions
{
MaxDegreeOfParallelism = 4
},
item => DoSomething(item));
ParallelOptions.MaxDegreeOfParallelism参数含义:
If your task is CPU-bound then you should see a pattern like this on a quad-core system:
ParallelOptions.MaximumDegreeOfParallelism = 1: use one full CPU or 25% CPU utilizationParallelOptions.MaximumDegreeOfParallelism = 2: use two CPUs or 50% CPU utilizationParallelOptions.MaximumDegreeOfParallelism = 4: use all CPUs or 100% CPU utilization
4. 如何等待Parallel.ForEach运行都结束
Parallel.ForEach<Item>(items, item => DoSomething(item));
Console.WriteLine("ddd");
是阻塞的,所以以上代码会在最后输出ddd。
如果是等多个Task,可以这样写:
var task1 = Task.Factory.StartNew(() => DoSomeWork());
var task2 = Task.Factory.StartNew(() => DoSomeWork());
var task3 = Task.Factory.StartNew(() => DoSomeWork());
Task.WaitAll(task1, task2, task3);
或者这样写:
Task.Factory.ContinueWhenAll(new[] { task1, task2, task3 }, tasks =>
{
foreach (Task<string> task in tasks)
{
Console.WriteLine(task.Result);
}
});
5. Task.Factory.StartNew和Parallel.ForEach可以嵌套使用吗
都可以嵌套使用,例如:
var task1 = Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
var task2 = Task.Factory.StartNew( () => Parallel.ForEach<Item>(items2, item => DoSomething2(item)));
Task.WaitAll(task1, task2);
6. Thread.Sleep还需要吗
以前,我们轮询的时候常常喜欢这样的写法:
while(true)
{
doSomework(); Thread.Sleep(1000);
}
这是一种代码的坏味道,Stackoverflow的讨论在这儿,解决方法是用WaitEvent替代,当然在C#中还是推荐用BlockingCollection替代。
6. TPL中闭包的陷阱
例如在下面的代码中 counter++存在线程不安全的问题。
int counter = 0; Task.Factory.StartNew( () =>
Parallel.ForEach(items,
new ParallelOptions
{
MaxDegreeOfParallelism = 4
},
item => {
DoSomething(item);
counter++;
});
);
应该改为:
Interlocked.Increment(ref successCount);
7. Lock锁带来的性能问题
性能问题首先要诊断,例如用条件编译打印出线程id和运行时序,可以知道所有线程的运行先后次序和等待情况。还可以借助工具来调试多线程问题。这里要说的锁的问题。如果你的程序用Parallel.ForEach貌似是并发的,但如果有用到Lock,那可能你所有的线程都在等待,性能将是一塌糊涂的。所以最好的方法是避免锁,保证Parallel.ForEach里面每一个对象不会用到竞争的资源/例如修改同一个对象。退而求其次的是用锁,但要非常小心。例如,lock(this),lock(typeof(mytype)),lock(“mylock”),如果lock的是public访问的,或者锁名字一样,将会造成问题。还有的人干脆来个大括号,一整段全都锁住。死锁有时候很难调试发现诊断,下面的代码有死锁:
// thread 1
lock(typeof(int)) {
Thread.Sleep(1000);
lock(typeof(float)) {
Console.WriteLine("Thread 1 got both locks");
} } // thread 2
lock(typeof(float)) {
Thread.Sleep(1000);
lock(typeof(int)) {
Console.WriteLine("Thread 2 got both locks");
}
}
8. TaskFactory.Startnew和异步async/await的不同
var Data = await Task.WhenAll(WebService1.Call(),
WebService2.Call(),
WebService3.Call());
关于TaskFactory.Startnew和异步async/await的不同,下面两文章已经讲的非常清楚了:
- http://msdn.microsoft.com/en-us/library/dd997423.aspx
- http://stackoverflow.com/questions/10285159/difference-between-the-tpl-async-await-thread-handling
在下面的情况下,推荐使用Task.Factory.FromAsync()因为异步I/O比同步的CPU等待等有效,特别是对于获取I/O的高伸缩性。
NetworkStream stream;
byte[] data;
int bytesRead; //using FromAsync
Task<int> readChunk = Task<int>.Factory.FromAsync (
stream.BeginRead, stream.EndRead,
data, bytesRead, data.Length - bytesRead, null); //using StartNew with blocking version
Task<int> readChunk2 = Task<int>.Factory.StartNew(() =>
stream.Read(data, bytesRead, data.Length - bytesRead));
9. 其它资源
C#并行库(TaskParallelLibrary)用法小结的更多相关文章
- C++ typedef用法小结 (※不能不看※)
C++ typedef用法小结 (※不能不看※) 第一.四个用途 用途一: 定义一种类型的别名,而不只是简单的宏替换.可以用作同时声明指针型的多个对象.比如:char* pa, pb; // 这多数不 ...
- 函数fgets和fputs、fread和fwrite、fscanf和fprintf用法小结 (转)
函数fgets和fputs.fread和fwrite.fscanf和fprintf用法小结 字符串读写函数fgets和fputs 一.读字符串函数fgets函数的功能是从指定的文件中读一个字符串到字符 ...
- typedef用法小结
typedef用法小结- - 注意:本文转自网络,版权归原作者所有. typedef typedef用法小结- - 这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等 ...
- TinyXML用法小结
TinyXML用法小结 1. 介绍 Tinyxml的官方网址:http://www.grinninglizard.com 官方介绍文档:http://www.grinninglizard.c ...
- pandas用法小结
前言 个人感觉网上对pandas的总结感觉不够详尽细致,在这里我对pandas做个相对细致的小结吧,在数据分析与人工智能方面会有所涉及到的东西在这里都说说吧,也是对自己学习的一种小结! pandas用 ...
- MVC图片上传详解 IIS (安装SSL证书后) 实现 HTTP 自动跳转到 HTTPS C#中Enum用法小结 表达式目录树 “村长”教你测试用例 引用provinces.js的三级联动
MVC图片上传详解 MVC图片上传--控制器方法 新建一个控制器命名为File,定义一个Img方法 [HttpPost]public ActionResult Img(HttpPostedFile ...
- TinyXML用法小结2
参考:http://www.cnblogs.com/hgwang/p/5833638.html TinyXML用法小结 1. 介绍 Tinyxml的官方网址:http://www.grinn ...
- 转载:Hadoop排序工具用法小结
本文转载自Silhouette的文章,原文地址:http://www.dreamingfish123.info/?p=1102 Hadoop排序工具用法小结 发表于 2014 年 8 月 25 日 由 ...
- [No000010]Ruby 中一些百分号(%)的用法小结
#Ruby 中一些百分号(%)的用法小结 #这篇文章主要介绍了Ruby 中一些百分号(%)的用法小结,需要的朋友可以参考下 what_frank_said = "Hello!"#% ...
随机推荐
- Java for LeetCode 059 Spiral Matrix II
Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order. For ...
- GLSL的qualifier
uniform:从应用程序到vertex shader 到fragment shader都能使用,但是值一直不变: varying:从vertex shader到fragment shader,在fr ...
- vmware安装linux.iso
安装方法 : .进入Fedora后,在虚拟机选项栏中选VM->install vmware tools 拷贝VMware Tools.tar.gz到指定文件夹,解压缩 进入超级终端:在-> ...
- tcp连接管理
[root@ok etc]# cat /proc/sys/net/core/netdev_max_backlog 每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目 ...
- Linux中信号量处理
参考文章: http://blog.csdn.net/qinxiongxu/article/details/7830537/ 信号量一. 什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个 ...
- hdu 2199:Can you solve this equation?(二分搜索)
Can you solve this equation? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ( ...
- sdut 2413:n a^o7 !(第三届山东省省赛原题,水题,字符串处理)
n a^o7 ! Time Limit: 1000MS Memory limit: 65536K 题目描述 All brave and intelligent fighters, next you w ...
- Java Hour 16 来个CURD吧!
有句名言,叫做10000小时成为某一个领域的专家.姑且不辩论这句话是否正确,让我们到达10000小时的时候再回头来看吧. 突然想到我最近一直在追的小说,作者每天都会更新两章,而且质量挺高.所以从这篇开 ...
- hdu 4289 最大流拆点
大致题意: 给出一个又n个点,m条边组成的无向图.给出两个点s,t.对于图中的每个点,去掉这个点都需要一定的花费.求至少多少花费才能使得s和t之间不连通. 大致思路: 最基础的拆点最大 ...
- 图解VS2010打包全过程(转)
最近刚刚打包发布了用VS2010开发的一个收费系统,借此讲一讲打包过程,供大家参考. 首先打开已经完成的工程,如图: 下面开始制作安装程序包. 第一步:[文件]——[新建]——[项目]——安装项目. ...