【问题标题】:Parallel.ForEach apparently cloning reference type?Parallel.ForEach 显然是克隆引用类型?
【发布时间】:2014-02-05 16:26:29
【问题描述】:

我有一个简单的方法:

private IEnumerable<OrderResult> SubmitOrders(IEnumerable<OrderItem> items)
    {
        ConcurrentBag<OrderResult> results = new ConcurrentBag<OrderResult>();

        Parallel.ForEach(items, item =>
            {
                OrderResult result = SubmitOrder(item);
                results.Add(result);
            });

        return results;
    }

为了添加更多上下文,如果订单提交成功,上面引用的 SubmitOrder(item) 方法会将“item”对象上的“Sent”(DateTime?)属性更改为 DateTime.Now。

在 Parallel.ForEach 循环中,在 SubmitOrder(item) 之后,我可以看到“Sent”属性已正确更新。

但是,如果我检查传入的“items”参数中的对象,则没有任何“Sent”属性被更新。就好像传递到 Parallel.ForEach 循环中的项目不是原始“项目”集合中的项目。

这是为什么?如果传入集合中的对象在循环中被修改,我希望这些更改会反映在原始集合中的项目中,但它们似乎没有。

【问题讨论】:

  • 而且OrderItem 不是值类型,对吧?
  • 不,OrderItem 是一个类。但是在循环中分配的“Sent”属性是 Nullable 类型(结构),我想知道这是否是问题所在。
  • 什么目标框架?
  • Visual Studio 2012 中的编译器发生了变化,改变了处理闭包的方式,从而复制了一些变量,而不仅仅是引用。但是,我看不出这会对您产生什么影响,因为没有任何其他代码(在 ForEach 之外)可以修改集合中的项目;所以即使有副本,也应该和原件一样。
  • 如果这是一个克隆/深度复制的对象,对我来说是有意义的,但我不确定如何证明是这样。或者,甚至为什么会这样?

标签: .net parallel.foreach


【解决方案1】:

items的参数类型为IEnumerable&lt;OrderItem&gt;。如果尚未枚举items,并且枚举它们会创建新对象,那么这可能是原因,因为您在SubmitOrder() 中更新的对象与下次items 中的对象不同列举的。这是一个完整的 LINQPad C# 程序,它演示了我的意思。

void Main()
{
    IEnumerable<OrderItem> itemsAsIEnumerable =
        Enumerable
            .Range(1, 5)
            .Select(i => new OrderItem() { ItemNumber = i });
    SubmitOrders(itemsAsIEnumerable);
    itemsAsIEnumerable.Dump();
        /* Displays:
        ItemNumber Sent
        1 null 
        2 null 
        3 null 
        4 null 
        5 null 
        */

    IEnumerable<OrderItem> itemsAsList =
        Enumerable
            .Range(1, 5)
            .Select(i => new OrderItem() { ItemNumber = i })
            .ToList();
    SubmitOrders(itemsAsList);
    itemsAsList.Dump();
        /* Displays:
        ItemNumber Sent
        1 2/5/2014 10:01:58 AM
        2 2/5/2014 10:01:58 AM
        3 2/5/2014 10:01:58 AM
        4 2/5/2014 10:01:58 AM
        5 2/5/2014 10:01:58 AM
        */      
}

private IEnumerable<OrderResult> SubmitOrders(IEnumerable<OrderItem> items)
{
    ConcurrentBag<OrderResult> results = new ConcurrentBag<OrderResult>();

    Parallel.ForEach(items, item =>
        {
            OrderResult result = SubmitOrder(item);
            results.Add(result);
        });

    return results;
}

private OrderResult SubmitOrder(OrderItem item)
{
    item.Sent = DateTime.Now;
    return new OrderResult();
}

public class OrderItem
{
    public int ItemNumber { get; set; }

    public DateTime? Sent { get; set; }
}

public class OrderResult
{
}

【讨论】:

  • 好电话,本!我认为这是问题所在。我只是通过在传递给方法的 IEnumerable 上调用 .ToList() 进行了快速测试,并且得到了预期的行为。
猜你喜欢
  • 2014-01-09
  • 2010-11-23
  • 1970-01-01
  • 1970-01-01
  • 2018-03-26
  • 1970-01-01
  • 2012-02-19
  • 2011-04-16
  • 2011-01-19
相关资源
最近更新 更多