2

基本上,我有一个对象集合,我将它切成小集合,并同时在每个小集合的线程上做一些工作。

int totalCount =  SomeDictionary.Values.ToList().Count;
int singleThreadCount = (int)Math.Round((decimal)(totalCount / 10));
int lastThreadCount = totalCount - (singleThreadCount * 9);

Stopwatch sw = new Stopwatch();

Dictionary<int,Thread> allThreads = new Dictionary<int,Thread>();
List<rCode> results = new List<rCode>();

for (int i = 0; i < 10; i++)
{
    int count = i;

    if (i != 9)
    {
        Thread someThread = new Thread(() =>
        {
            List<rBase> objects =  SomeDictionary.Values
                                          .Skip(count * singleThreadCount)
                                          .Take(singleThreadCount).ToList();

            List<rCode> result = objects.Where(r => r.ZBox != null)
            .SelectMany(r => r.EffectiveCBox, (r, CBox) => new rCode
                                {
                                    RBox = r,
                                    // A Zbox may refer an object that can be 
                                    // shared by many 
                                    // rCode objects even on different threads
                                    ZBox = r.ZBox,
                                    CBox = CBox
                                }).ToList();

            results.AddRange(result);
        });

        allThreads.Add(i, someThread);
        someThread.Start();
    }
    else 
    {
        Thread someThread = new Thread(() =>
        {
            List<rBase> objects =  SomeDictionary.Values
                                           .Skip(count * singleThreadCount)
                                           .Take(lastThreadCount).ToList();

            List<rCode> result = objects.Where(r => r.ZBox != null)
            .SelectMany(r => r.EffectiveCBox, (r, CBox) => new rCode
                        {
                            RBox = r,
                            // A Zbox may refer an object that 
                            // can be shared by many 
                            // rCode objects even on different threads
                            ZBox = r.ZBox, 
                            CBox = CBox
                        }).ToList();

            results.AddRange(result);
        });

        allThreads.Add(i, someThread);
        someThread.Start();
    }
}

sw.Start();
while (allThreads.Values.Any(th => th.IsAlive))
{ 
    if (sw.ElapsedMilliseconds >= 60000) 
    { 
        results = null; 
        allThreads.Values.ToList().ForEach(t => t.Abort()); 
        sw.Stop(); 
        break; 
    } 
}

return  results != null ? results.OrderBy(r => r.ZBox.Name).ToList():null;

所以,我的问题是,有时,在返回结果之前执行 OrderBy 操作时,我得到一个空引用异常,我无法确定异常到底在哪里,我按下返回,单击执行此操作的相同按钮再次相同的数据,它的工作原理!..如果有人可以帮助我确定这个问题,我将不胜感激。注意:Zbox 可能引用一个对象,即使在不同的线程上也可以由许多 rCode 对象共享,这可能是问题吗?因为我无法在测试时确定这一点,因为发生的错误不是确定性的。

4

3 回答 3

2

尽管我不同意答案,但在所选答案中正确找到了该错误。您应该切换到使用并发集合。在您的情况下是 ConcurrentBag 或 ConcurrentQueue。其中一些是(部分)无锁的,以获得更好的性能。而且它们提供了更多可读性和更少的代码,因为您不需要手动锁定。

如果您避免手动创建线程和手动分区,您的代码大小也会减少一半以上,可读性也会增加一倍;

Parallel.ForEach(objects, MyObjectProcessor);

public void MyObjectProcessor(Object o)
{
  // Create result and add to results
}

如果要使用 Parallel.ForEach 限制线程数,请使用 ParallelOptions 对象............

于 2012-09-14T14:15:04.960 回答
1

好吧,这里有一个明显的问题:

results.AddRange(result);

您正在从多个线程更新列表的位置。尝试使用锁:

object resultsLock = new object(); // globally visible
...
lock(resultsLock) 
{
    results.AddRange(result);
}
于 2012-09-14T10:11:30.903 回答
0

我想结果中的问题= null

while (allThreads.Values.Any(th => th.IsAlive))
    { if (sw.ElapsedMilliseconds >= 60000) { results = null;  allThreads.Values.ToList().ForEach(t => t.Abort());

如果线程没有以超过 60000 毫秒的速度完成,则结果变为等于 null 并且您不能调用 results.OrderBy(r => r.ZBox.Name).ToList(); 它抛出异常

你应该添加类似的东西

if (results != null) 
  return  results.OrderBy(r => r.ZBox.Name).ToList();
else
  return null;
于 2012-09-14T09:45:23.440 回答