带有外部参数的Task.Run循环被循环更改

时间:2019-04-26 17:14:34

标签: c# .net multithreading thread-safety task

我有这段代码(简化的)可以在100个不同的并行线程上处理参数数组,但是变量x和y在线程中使用时会被线程内的循环更改。如果我使用1个线程运行该函数,那么它将起作用。

我还尝试将参数放入ConcurrentBag中,并使用foreach进行循环,但结果相同,线程中混合了参数。

List<Task> tasks = new List<Task>();
var listConcurentBag = new ConcurrentBag<int>();
int nThreadCount = 0;

for (x=0; x<1000; x++)
  for (y=0; y<1000; y++)
  {
     int x1=x;
     int y2=y;

     Task t = Task.Run(() =>
     {         
        int param1=x1;
        int param2=y2;

        // some calculations with param1 and param2

        listConcurentBag.Add(result);
     }); // tasks

     tasks.Add(t);
     nThreadCount++;

   if (nThreadCount == 100) // after 100 threads started, wait
   {
       nThreadCount = 0;
       Task.WaitAll(tasks.ToArray());
       tasks.Clear();
   }
 }

2 个答案:

答案 0 :(得分:2)

您应该使用Microsoft的Reactive Framework(又名Rx)-NuGet System.Reactive并添加using System.Reactive.Linq;-然后您可以执行以下操作:

var query =
    from x in Observable.Range(0, 1000)
    from y in Observable.Range(0, 1000)
    from r in Observable.Start(() => GetResult(x,  y))
    select new { x, y, r };

IDisposable subscription =
    query
        .Buffer(100)
        .Subscribe(results =>
        {
            /* do something with each buffered list of results */
        });

现在,这与您当前的代码并不完全相同,但是使用线程池的最大容量后,它会立即为您提供100个结果块。

您可以更改它以设置并发性:

var query =
    from x in Observable.Range(0, 1000)
    from y in Observable.Range(0, 1000)
    select Observable.Start(() => new { x, y, r = GetResult(x,  y) });

IDisposable subscription =
    query
        .Merge(maxConcurrent: 100) // limit to 100 threads
        .Buffer(count: 100) // produce 100 results at a time
        .Subscribe(results =>
        {
            /* do something with the list of results */
        });

如果您想在代码自然完成之前停止代码,只需调用subscription.Dispose();

Rx确实会产生更清晰的代码,恕我直言。

答案 1 :(得分:1)

我有一个替代实施的建议,您可能会发现也可能不适合您的需求。而不是分批处理任务 您可以将nested-for循环表示为单个可枚举,然后将其提供给内置方法Parallel.ForEach进行并行处理。

private IEnumerable<(int, int)> GetNestedFor()
{
    for (int x = 0; x < 1000; x++)
    {
        for (int y = 0; y < 1000; y++)
        {
            yield return (x, y); // return a ValueTuple<int, int>
        }
    }
}

ThreadPool.SetMinThreads(100, 100);
var options = new ParallelOptions() { MaxDegreeOfParallelism = 100 };
Parallel.ForEach(GetNestedFor(), options, item =>
{
    int param1 = item.Item1;
    int param2 = item.Item2;
    Console.WriteLine($"Processing ({param1}, {param2})");
    Thread.Sleep(100); // Simulate some work
});

输出:

  

处理(0,1)
  处理中(0,2)
  处理中(0,0)
  处理中(0,3)
  ...
  处理中(0,998)
  处理中(0,997)
  处理中(0,999)
  处理中(1,0)
  处理中(1,1)
  ...
  处理中(999,999)
  处理中(999,998)