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 ...
随机推荐
- Python:字典
#!/usr/bin/python3 #dict 字典 #字典是可变的 dict1 = {"name":"张三","age":22} pri ...
- [转载] java中byte数组与int,long,short间的转换
文章转载自http://blog.csdn.net/leetcworks/article/details/7390731 package com.util; /** * * <ul> * ...
- Microsoft Visual Studio 2010 遇到了异常,可能是由某个扩展导致的。 转载
问题: 今天打开好久没用的Microsoft Visual Studio 2010 ,刚才创建了一个C++工程,错误就出现了. 只要在VS2010源码编辑器中输入一个字符,它就报错 ":Mi ...
- Excel里面将头尾第一个字母保留,去除中间的用*号代替,主要是REPT函数的应用,一开始我还以为要自己写个自定义函数
Excel里面将头尾第一个字母保留,去除中间的用*号代替,主要是REPT函数的应用,一开始我还以为要自己写个自定义函数 =LEFT(A1,1)&REPT("*",(LEN( ...
- CentOS 6.4 64位 安装 mysql 5.6.24
下载安装包 由于官网访问及版本选择下载不太方便,使用 suho 的源进行下载 http://mirrors.sohu.com/mysql/MySQL-5.6/ 下载如下三个安装包: MySQL-ser ...
- sencha touch中按钮的ui配置选项值及使用效果
- shell 1变量注意点
定义变量时,变量名不加美元符号($),如: variableName="value" 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样. 删除变量 使用 un ...
- paramiko学习
1. ssh简介 2. ssh私有key/共有key的区别 3. paramiko的常规使用
- windows通过Composer安装yii2
1. php.ini 中;extension=php_openssl.dll(取消注释,不然在安装composer过程中会报错) 集成环境最好去php目录中打开php.ini文件,确定;extensi ...
- Asp.net MVC的Controller激活理解【学习笔记】
DefaultControllerFactory 是MVC默认的Controller查找和激活工厂类我们可以通过自定义ControllerFactory替换DefaultControllerFacto ...