【问题标题】:List divide and conquer - Pass by value or reference列表分而治之 - 按值或引用传递
【发布时间】:2011-03-23 22:11:34
【问题描述】:

我不在 .NET 4 上。

我从数据源中获得了一个巨大的列表。当列表中的元素个数大于 X 时,我喜欢对列表进行分区,将每个分区分配给一个线程。处理完分区后,我喜欢合并它们。

            var subsets = list.PartitionEager(50000);

            //var subsets = list.Partition<T>(50000);

            Thread[] threads = new Thread[subsets.Count()];
            int i = 0;
            foreach (var set in subsets)
            {
                threads[i] = new Thread(() => Convertor<T>(set));
                threads[i].Start();
                i++;
            }

            for (int j = 0; j < i; j++)
            {
                threads[j].Join();
            }

Convertor 方法是一个静态方法,它接受一个列表并进行一些查找。

   public static void Convertor<T>(List<T> list) where T : IInterface        {

        foreach (var element in list)
        {
            **// do some lookup and assing a value to element
            // then do more lookup and assign a value to element**
        }

    }

当我运行这段代码时,即使我知道大多数元素都会被赋值。他们实际上是返回null。

我知道列表的副本将传递给方法,但对元素的任何更改都应反映在上层方法中。然而,这只发生在最终的子集中。

我什至添加了一些代码来将列表合并为一个。

                list.Clear();

                foreach (var set in subsets)
                {
                    list.AddRange(set);
                }

分区代码:

    public static List<List<T>> PartitionEager<T>(this List<T> source, Int32 size)
    {
        List<List<T>> merged = new List<List<T>>();
        for (int i = 0; i < Math.Ceiling(source.Count / (Double)size); i++)
        {
            merged.Add(new List<T>(source.Skip(size * i).Take(size)));
        }

        return merged;
    }

我做错了什么?如何解决这个问题?我想在查找后给元素赋值吗?这与同步或参数传递有关吗?

【问题讨论】:

  • 你的分区算法代码是什么?
  • 另外,每个集合中的值都不应该为空,你对它们做了什么?
  • 我正在查找一个对象并将结果附加到该对象。即使查找失败,我也会附加一个默认值。所以它根本不应该为空。

标签: c# multithreading list reference parameter-passing


【解决方案1】:

如果.NET 4 是一个选项,您可以只使用Parallel.ForParallel.ForEach。这些方法会自动为您处理分区,并在跨不同系统上的多个并发度的可扩展性方面提供许多其他优势。

【讨论】:

  • @user177883:您也可以通过安装 Rx Extensions 来使用 .NET 3.5 执行此操作,因为它们包含这些方法。
【解决方案2】:

看起来您在创建线程时遇到了modified closure。如果我是正确的,那么您的所有线程都会更新相同的(最后一个)集合。这样修改代码:

        foreach (var set in subsets)
        {
            var setLocalCopy = set;
            threads[i] = new Thread(() => Convertor<T>(setLocalCopy));
            threads[i].Start();
            i++;
        }

【讨论】:

  • 但是我如何处理本地副本?将值转换为对象的转换器。
  • @user,它不会是整个列表的本地副本,它只是set 引用的副本。它仍会更新您正在更新的相同项目。
  • 谢谢。就是这样。那么这里的教训是什么?为什么会这样?
  • @user,这种情况命名为modified closure,你可以google一下。当您将枚举器传递给某个惰性方法(在您的情况下为ThreadStart 委托)然后您修改枚举器之前执行委托时,通常会发生这种情况。例如,请参见此处:stackoverflow.com/questions/235455/access-to-modified-closure
猜你喜欢
  • 1970-01-01
  • 2012-01-16
  • 2012-10-14
  • 1970-01-01
  • 2013-08-17
  • 1970-01-01
  • 2013-11-09
  • 2012-02-25
  • 1970-01-01
相关资源
最近更新 更多