简体   繁体   English

具有外部参数的Task.Run循环通过循环更改

[英]Task.Run loop with external parameters are changed by loop

I have this code (simplified) to process an array of parameters on 100 different parallel threads, but the variables x and y gets changed by the loop within the threads while are used in threads. 我有这个代码(简化)来处理100个不同并行线程上的参数数组,但变量x和y在线程中被线程中的循环改变。 If I run the function with 1 thread then it's working. 如果我用1个线程运行该函数,那么它正在工作。

I also tried to put the parameters in a ConcurrentBag and making the loop with foreach but the same result, the parameters gets mixed in threads. 我还尝试将参数放在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();
   }
 }

You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; 您应该使用Microsoft的Reactive Framework(又名Rx) - NuGet System.Reactiveusing System.Reactive.Linq;添加using System.Reactive.Linq; - then you can do this: - 然后你可以这样做:

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 */
        });

Now this isn't strictly doing the same as your current code, but it is giving you blocks of 100 results as soon as they are available using the maximum capacity of the thread-pool. 现在这并不是与当前代码严格相同,但只要它们可用,就可以使用线程池的最大容量为您提供100个结果块。

You could change it to set the concurrency like this: 你可以改变它来设置这样的并发:

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 */
        });

If you want to stop the code before it naturally completes just call subscription.Dispose(); 如果你想在自然完成之前停止代码,只需调用subscription.Dispose(); .

Rx does tend to produce much cleaner code, IMHO. x确实会产生更清晰的代码,恕我直言。

I have a suggestion for an alternative implementation, that you may or may not find suitable for your needs. 我有一个替代实施的建议,您可能会或可能不会找到适合您的需求。 Instead of processing the tasks in batches of 100 you could express the nested-for loops as a single enumerable, and then feed it to the build-in method Parallel.ForEach to do the work of parallelism. 您可以将嵌套for循环表示为单个可枚举,而不是以100个批次处理任务,然后将其提供给内置方法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
});

Output: 输出:

Processing (0, 1) 处理(0,1)
Processing (0, 2) 处理(0,2)
Processing (0, 0) 处理(0,0)
Processing (0, 3) 处理(0,3)
... ...
Processing (0, 998) 处理(0,998)
Processing (0, 997) 处理(0,997)
Processing (0, 999) 处理(0,999)
Processing (1, 0) 处理(1,0)
Processing (1, 1) 处理(1,1)
... ...
Processing (999, 999) 加工(999,999)
Processing (999, 998) 处理(999,998)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM