Parallel.Foreach的并发问题解决方法-比如爬虫WebClient
场景五:线程局部变量
Parallel.ForEach 提供了一个线程局部变量的重载,定义如下:
public static ParallelLoopResult ForEach<TSource, TLocal>(
IEnumerable<TSource> source,
Func<TLocal> localInit,
Func<TSource, ParallelLoopState, TLocal,TLocal> body,
Action<TLocal> localFinally)
使用的示例:
public static List<R> Filtering<T,R>(IEnumerable<T> source)
{
var results = new List<R>();
using (SemaphoreSlim sem = new SemaphoreSlim(1))
{
Parallel.ForEach(source,
() => new List<R>(),
(element, loopstate, localStorage) =>
{
bool filter = filterFunction(element);
if (filter)
localStorage.Add(element);
return localStorage;
},
(finalStorage) =>
{
lock(myLock)
{
results.AddRange(finalStorage)
};
});
}
return results;
}
线程局部变量有什么优势呢?请看下面的例子(一个网页抓取程序):
public static void UnsafeDownloadUrls ()
{
WebClient webclient = new WebClient();
Parallel.ForEach(urls,
(url,loopstate,index) =>
{
webclient.DownloadFile(url, filenames[index] + ".dat");
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
});
}
通常第一版代码是这么写的,但是运行时会报错“System.NotSupportedException -> WebClient does not support concurrent I/O operations.”。这是因为多个线程无法同时访问同一个 WebClient 对象。所以我们会把 WebClient 对象定义到线程中来:
public static void BAD_DownloadUrls ()
{
Parallel.ForEach(urls,
(url,loopstate,index) =>
{
WebClient webclient = new WebClient();
webclient.DownloadFile(url, filenames[index] + ".dat");
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
});
}
修改之后依然有问题,因为你的机器不是服务器,大量实例化的 WebClient 迅速达到你机器允许的虚拟连接上限数。线程局部变量可以解决这个问题:
public static void downloadUrlsSafe()
{
Parallel.ForEach(urls,
() => new WebClient(),
(url, loopstate, index, webclient) =>
{
webclient.DownloadFile(url, filenames[index]+".dat");
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
return webclient;
},
(webclient) => { });
}
这样的写法保证了我们能获得足够的 WebClient 实例,同时这些 WebClient 实例彼此隔离仅仅属于各自关联的线程。
虽然 PLINQ 提供了 ThreadLocal<T> 对象来实现类似的功能:
public static void downloadUrl()
{
var webclient = new ThreadLocal<WebClient>(()=> new WebClient ());
var res =
urls
.AsParallel()
.ForAll(
url =>
{
webclient.Value.DownloadFile(url, host[url] +".dat"));
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
});
}
但是请注意:ThreadLocal<T> 相对而言开销更大!
--
场景五:退出操作 (使用 Parallel.ForEach)
Parallel.ForEach 有个重载声明如下,其中包含一个 ParallelLoopState 对象:
public static ParallelLoopResult ForEach<TSource >(
IEnumerable<TSource> source,
Action<TSource, ParallelLoopState> body)
ParallelLoopState.Stop() 提供了退出循环的方法,这种方式要比其他两种方法更快。这个方法通知循环不要再启动执行新的迭代,并尽可能快的推出循环。
ParallelLoopState.IsStopped 属性可用来判定其他迭代是否调用了 Stop 方法。
示例:
public static boolean FindAny<T,T>(IEnumerable<T> TSpace, T match) where T: IEqualityComparer<T>
{
var matchFound = false;
Parallel.ForEach(TSpace,
(curValue, loopstate) =>
{
if (curValue.Equals(match) )
{
matchFound = true;
loopstate.Stop();
}
});
return matchFound;
}
ParallelLoopState.Break() 通知循环继续执行本元素前的迭代,但不执行本元素之后的迭代。最前调用 Break 的起作用,并被记录到 ParallelLoopState.LowestBreakIteration 属性中。这种处理方式通常被应用在一个有序的查找处理中,比如你有一个排序过的数组,你想在其中查找匹配元素的最小 index,那么可以使用以下的代码:
public static int FindLowestIndex<T,T>(IEnumerable<T> TSpace, T match) where T: IEqualityComparer<T>
{
var loopResult = Parallel.ForEach(source,
(curValue, loopState, curIndex) =>
{
if (curValue.Equals(match))
{
loopState.Break();
}
});
var matchedIndex = loopResult.LowestBreakIteration;
return matchedIndex.HasValue ? matchedIndex : -1;
} 更多:
http://www.tuicool.com/articles/jqaUVj
Parallel.Foreach的并发问题解决方法-比如爬虫WebClient的更多相关文章
- C#并发实战Parallel.ForEach使用
前言:最近给客户开发一个伙食费计算系统,大概需要计算2000个人的伙食.需求是按照员工的预定报餐计划对消费记录进行检查,如有未报餐有刷卡或者有报餐没刷卡的要进行一定的金额扣减等一系列规则.一开始我的想 ...
- 第九节:深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)
一. 并行编程 1. 区分串行编程和串行编程 ①. 串行编程:所谓的串行编程就是单线程的作用下,按顺序执行.(典型代表for循环 下面例子从1-100按顺序执行) ②. 并行编程:充分利用多核cpu的 ...
- Python爬虫编程常见问题解决方法
Python爬虫编程常见问题解决方法: 1.通用的解决方案: [按住Ctrl键不送松],同时用鼠标点击[方法名],查看文档 2.TypeError: POST data should be bytes ...
- .NET4中多线程并行方法Parallel.ForEach
原文发布时间为:2011-12-10 -- 来源于本人的百度文章 [由搬家工具导入] namespace ForEachDemo{ using System; using System.I ...
- Parallel.Foreach
随着多核时代的到来,并行开发越来越展示出它的强大威力! 使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Ta ...
- [译]何时使用 Parallel.ForEach,何时使用 PLINQ
原作者: Pamela Vagata, Parallel Computing Platform Group, Microsoft Corporation 原文pdf:http://download.c ...
- Parallel.ForEach , ThreadPool.QueueUserWorkItem
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Parallel.ForEach() 并行循环
现在的电脑几乎都是多核的,但在软件中并还没有跟上这个节奏,大多数软件还是采用传统的方式,并没有很好的发挥多核的优势. 微软的并行运算平台(Microsoft’s Parallel Computing ...
- Parallel.Foreach的全部知识要点【转】
简介 当需要为多核机器进行优化的时候,最好先检查下你的程序是否有处理能够分割开来进行并行处理.(例如,有一个巨大的数据集合,其中的元素需要一个一个进行彼此独立的耗时计算). .net framewor ...
随机推荐
- html》meta标签笔记
meta是html语言head区的一个辅助性标签.也许你认为这些代码可有可无.其实如果你能够用好meta标签,会给你带来意想不到的效果,meta标签的作用有:搜索引擎优化(SEO),定义页面使用语言, ...
- Android学习4—短信发送器的实现
界面预览: 由图中可以看出,此APP需要的组件有:两个TextView,一个用于显示手机号码的标题,另一个用于显示短信内容的标题. ...
- TCP/IP笔记 应用层(1)——DNS
1. DNS DNS(Domain Name System ):域名系统,是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住能够被机 ...
- 【python】python程序分行写符号
方法1,用括号 >>> 3+4+(5+ 6)18 方法2 用 \ >>> 3+5+\ 412
- 2016022605 - redis订阅发布
Redis的实现消息队列功能,消息发布者发送的消息,消息接收者接收发布者的消息.由该消息传送的链路被称为通道.在Redis客户端可以订阅任何数目的通道. 案例:给出一个客户端订阅一个通道名为redis ...
- 【C语言】重定向和文件
重定向和文件 一.相关基础知识 重定向:在计算机领域,重定向是大多数命令行解释器所具有的功能,包括各种可以将标准流重定向用户规定地点的Unix shells. 输入重定向:可以使程序能够使用文件代替键 ...
- 编译u-boot命令和u-boot常用命令
一.编译u-boot命令 1.配置开发板 #make TQ2440_config 2.编译 #make all 3.交叉编译器是crosstools_3.4.5_softfloat” 使用4.3.3版 ...
- SQL语句の循环添加数据
declare @i intset @i=1while @i<=1000begininsert into News_ITM(title,msg,subDateTime,author,imageP ...
- Linux下Java 编译运行说明
命令行环境下Java编译运行 1. java的运行机制的基本概念: 源文件 也就是我们熟知的.java文件. 类文件 .class文件是编译器由.java文件编译而成.众所周知,Java的跨平台性在 ...
- Hdu5517 Triple
Description Given the finite multi-set \(A\) of \(n\) pairs of integers, an another finite multi-set ...