【问题标题】:Why this difference between foreach vs Parallel.ForEach?为什么 foreach 与 Parallel.ForEach 之间存在这种差异?
【发布时间】:2013-10-21 11:05:03
【问题描述】:

谁能用简单的语言向我解释为什么我在使用 foreach 时得到一个大约 65 k 的文件,而在使用 Parallel.ForEach 时得到一个超过 3 GB 的文件?

foreach 的代码:

// start node xml document
var logItems = new XElement("log", new XAttribute("start", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss")));
var products = new ProductLogic().SelectProducts();
var productGroupLogic = new ProductGroupLogic();
var productOptionLogic = new ProductOptionLogic();
// loop through all products
foreach (var product in products)
{
    // is in a specific group
    var id = Convert.ToInt32(product["ProductID"]);
    var isInGroup = productGroupLogic.GetProductGroups(new int[] { id }.ToList(), groupId).Count > 0;
    // get product stock per option
    var productSizes = productOptionLogic.GetProductStockByProductId(id).ToList();
    // any stock available
    var stock = productSizes.Sum(ps => ps.Stock);
    var hasStock = stock > 0;
    // get webpage for this product
    var productUrl = string.Format(url, id);
    var htmlPage = Html.Page.GetWebPage(productUrl);
    // check if there is anything to log
    var addToLog = false;
    XElement sizeElements = null;
    // if has no stock or in group
    if (!hasStock || isInGroupNew)
    {
        // page shows => not ok => LOG!
        if (!htmlPage.NotFound) addToLog = true;
    }
    // if page is ok
    if (htmlPage.IsOk)
    {
        sizeElements = GetSizeElements(htmlPage.Html, productSizes);
        addToLog = sizeElements != null;
    }
    if (addToLog) logItems.Add(CreateElement(productUrl, htmlPage, stock, isInGroup, sizeElements));
}
// save
var xDocument = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("log", logItems));
xDocument.Save(fileName);

并行代码的使用是一个小改动,只是将 foreach 替换为 Parallel.ForEach:

// loop through all products
Parallel.ForEach(products, product =>
{
    ... code ...
};

GetSizeElements 和 CreateElements 方法都是静态的。

更新1 我用锁使 GetSizeElements 和 CreateElements 方法成为线程安全的,也没有帮助。

更新2 我得到解决问题的答案。这很好。但我想获得更多关于为什么这些代码创建一个比 foreach 解决方案大得多的文件的见解。我正在尝试更了解代码在使用线程时的工作方式。这样我可以获得更多的洞察力,并且我可以学会避免这些陷阱。

【问题讨论】:

  • 尽量简化你的代码。目前,您的代码中包含许多很可能与错误无关的代码,但这让我们很难处理。
  • 你在哪里定义了isInGroupNew,我看不到,我猜它超出了foreach范围吧?

标签: c# .net foreach parallel.foreach


【解决方案1】:

有一点很突出:

if (addToLog) 
  logItems.Add(CreateElement(productUrl, htmlPage, stock, isInGroup, sizeElements));

logItems 不安全。这可能是您的核心问题,但还有很多其他可能性。

你有输出文件,寻找差异。

【讨论】:

  • logItems 是一个 XElement 类。这与线程安全有什么关系?您的意思是 Add 方法不是线程安全的吗?但是,如果该方法不是线程安全的,那到底是什么,将会添加所有不同的元素。为什么这会成为一个问题?
  • @royu 这是一个问题,因为当两个线程同时调用Add() 时,可能会发生意想不到的事情。
  • 那么默认情况下它不是线程安全的原因是什么?
  • 如果按照 Mecek 的建议进行调整,那么它可以工作。所以 Add 方法似乎不是问题......
  • 只有当里面的代码是线程安全的时,你才能把foreach()改成Parallel.ForEach()。您的代码不是,以多种方式。您必须分析所有共享数据并解决问题。我不确定为什么@mecek 的答案有效。也许没有。
【解决方案2】:

尝试在 foreach 循环中定义以下参数。

var productGroupLogic = new ProductGroupLogic();
var productOptionLogic = new ProductOptionLogic();

我认为只有两个被并行 foreach 循环中的所有线程使用,结果不必要地相乘。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2013-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    相关资源
    最近更新 更多