场景五:线程局部变量

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的更多相关文章

  1. C#并发实战Parallel.ForEach使用

    前言:最近给客户开发一个伙食费计算系统,大概需要计算2000个人的伙食.需求是按照员工的预定报餐计划对消费记录进行检查,如有未报餐有刷卡或者有报餐没刷卡的要进行一定的金额扣减等一系列规则.一开始我的想 ...

  2. 第九节:深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)

    一. 并行编程 1. 区分串行编程和串行编程 ①. 串行编程:所谓的串行编程就是单线程的作用下,按顺序执行.(典型代表for循环 下面例子从1-100按顺序执行) ②. 并行编程:充分利用多核cpu的 ...

  3. Python爬虫编程常见问题解决方法

    Python爬虫编程常见问题解决方法: 1.通用的解决方案: [按住Ctrl键不送松],同时用鼠标点击[方法名],查看文档 2.TypeError: POST data should be bytes ...

  4. .NET4中多线程并行方法Parallel.ForEach

    原文发布时间为:2011-12-10 -- 来源于本人的百度文章 [由搬家工具导入] namespace ForEachDemo{    using System;    using System.I ...

  5. Parallel.Foreach

    随着多核时代的到来,并行开发越来越展示出它的强大威力! 使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Ta ...

  6. [译]何时使用 Parallel.ForEach,何时使用 PLINQ

    原作者: Pamela Vagata, Parallel Computing Platform Group, Microsoft Corporation 原文pdf:http://download.c ...

  7. Parallel.ForEach , ThreadPool.QueueUserWorkItem

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  8. Parallel.ForEach() 并行循环

    现在的电脑几乎都是多核的,但在软件中并还没有跟上这个节奏,大多数软件还是采用传统的方式,并没有很好的发挥多核的优势. 微软的并行运算平台(Microsoft’s Parallel Computing ...

  9. Parallel.Foreach的全部知识要点【转】

    简介 当需要为多核机器进行优化的时候,最好先检查下你的程序是否有处理能够分割开来进行并行处理.(例如,有一个巨大的数据集合,其中的元素需要一个一个进行彼此独立的耗时计算). .net framewor ...

随机推荐

  1. windows进程函数试炼

    实践一下windows进程相关函数: 代码如下: // test__getinformation.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h&quo ...

  2. wel

    欢迎来到mathant.com 这个网站是什么 这个网站是我搭建在阿里云vps上的个人网站.目前的用途是充当个人博客和云存储,当然它的功能不止如此.我会在以后的日子里完善他,希望他能变得更好.目前我在 ...

  3. 24种设计模式--模版方法模式【Template Method Pattern】

    周三,9:00,我刚刚坐到位置,打开电脑准备开始干活.“小三,小三,叫一下其它同事,到会议室,开会”老大跑过来吼,带着淫笑.还不等大家坐稳,老大就开讲了,“告诉大家一个好消息,昨天终于把牛叉模型公司的 ...

  4. C#程序中:如何修改xml文件中的节点(数据)

    要想在web等程序中实现动态的数据内容给新(如网页中的Flash),不会更新xml文件中的节点(数据)是远远不够的,今天在这里说一个简单的xml文件的更新,方法比较基础,很适合初学者看的,保证一看就懂 ...

  5. js 中对象属性的特性

    数据属性: 数据属性包含一个数据值的位置,在这个位置可以读取和写入值. 4个描述的行为特性: writable  表示能否修改属性的值.默认为true Enumerable 表示能否过过for in循 ...

  6. jQuery的延迟对象

    之前看别人的demo,发现在延迟对象被resolve时要执行的代码,有时会写在deferred.then方法里执行,有时会写在deferred.done方法里执行. 这让对延迟对象一知半解的我非常困惑 ...

  7. CentOS如何查看端口是被哪个应用/进程占用

    有时启动应用时会发现端口已经被占用,或者是感觉有些端口自己没有使用却发现是打开的.这时我们希望知道是哪个应用/进程在使用该端口. CentOS下可以用netstat或者lsof查看,Windows下也 ...

  8. 【CSS sprites (CSS图片精灵) 详解】

    本文包含 CSS sprites 简介.原理.适用在哪些类型的网页制作中.背景图片的 position 值如何确定以及制作 sprites 的技巧. [CSS sprites 简介] CSS Spri ...

  9. c++的四种强制类型转换

    http://hb.qq.com/a/20110722/001452.htm ...... C风格的强制类型转换(Type Cast)很简单,不管什么类型的转换统统是:TYPE b = (TYPE)a ...

  10. Asp.net MVC中三大描述对象之ActionDescriptor 以及继承类ReflectedControllerDescriptor

    ActionDescriptor抽象类中几个基本的属性: ControllerName:被描述的Controller名称,去除后缀Controller的名称.例如:HomeController则为Ho ...