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 ...
随机推荐
- js 中特殊形势的函数-匿名函数的应用
javascript中的匿名函数,那什么叫做匿名函数? 匿名函数就是没有函数名称:演示代码: <script> function(x,y){ return x+y //这个就是一个匿名函数 ...
- Android中使用WebView与JS交互全解析
1.概述首先,需要提出一个概念,那就是hybrid,主要意思就是native原生Android和h5混合开发.为什么要这样做呢?大家可以想象一下针对于同一个活动,如果使用纯native的开发方式,An ...
- 开发语言大PK:php和Java哪个更好?
Java通过jdbc来访问数据库,通过不同的数据库厂商提供的数据库驱动方便地访问数据库.访问数据库的接口比较统一. PHP对于不同的数据库采用不同的数据库访问接口,所以数据库访问代码的通用性不强.例如 ...
- jQuery监听键盘事件及相关操作使用
一.首先需要知道的是: 1.keydown() keydown事件会在键盘按下时触发. 2.keyup() keyup事件会在按键释放时触发,也就是你按下键盘起来后的事件 3.keypress() k ...
- Cinder-1 TinderBox
Cinder:http://libcinder.org/,当前版本是0.8.5,代码托管位置:https://github.com/cinder/Cinder.git 下载Cinder之后,其目录结构 ...
- WPF后台访问XAML元素
当我们需要从后台访问xaml文件时,我们可以通过这样的方式来操作: private void button1_Click(object sender, RoutedEventArgs e) { Sys ...
- Data Guard配置后续检查
Data Guard配置后续检查 1.打开生产端节点192.166.1.190上的数据库实例: SQL>startup; 2.打开容灾端节点192.166.1.60上的数据库实例: SQL> ...
- node案例
http://www.cnblogs.com/wewe/archive/2010/03/19/1685658.html http://www.laonan.net/blog/69/ http://cn ...
- Long Long Message
poj2774:http://poj.org/problem?id=2774 题意:求两个串的最长公共子串. 题解:求出后缀数组,然后求height数组,找出最大的值,并且这两个子串在不同的原串中即可 ...
- Initializing Spring root WebApplicationContext
最近 我部署ssh项目的时候经常出现这样的问题,我的解决办法是 log4j:WARN No appenders could be found for logger (org.springframewo ...