正确实现并行 foreach 循环并同时继续其他工作时出现问题

问题描述 投票:0回答:1

我想并行执行许多长时间运行的 CPU 和 IO 密集进程(如

Thread.Sleep()
所示)。这就是我使用
Parallel.Foreach
循环的原因。一段时间内不需要这些结果。这就是为什么我想继续不同的工作并将此任务任务(可以这么说)“挂起”到不同的线程,而每个
Parallel.Foreach
在不同的线程上再次执行。这就是为什么我返回封装在
Parallel.Foreach
- 语句中的
Task.Run()
。代码如下:

internal class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine($"Before ParallelForeach on thread: {Environment.CurrentManagedThreadId}"); //Main debug 1
        await TaskContainingParallelForeach();
        Console.WriteLine($"After  ParallelForeach on thread: {Environment.CurrentManagedThreadId}"); //Main debug 2
        int counter = 0;
        while (counter < 7)
        {
            Console.WriteLine($"{counter}. one second gone on thread:  {Environment.CurrentManagedThreadId} at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
            Thread.Sleep(1000);
            counter++;
        }

        Console.ReadKey();
    }

    public static Task TaskContainingParallelForeach()
    {
        return Task.Run(() =>
        {
            Console.WriteLine($"Inside ParallelForeach on thread: {Environment.CurrentManagedThreadId}");
            int[] numbers = { 1, 3, 2 };

            Parallel.ForEach(numbers, (num) =>
            {
                Console.WriteLine($"{num} is running on thread: {Environment.CurrentManagedThreadId}");
                Thread.Sleep(num * 1000);
                Console.WriteLine($"{num}    stopped on thread: {Environment.CurrentManagedThreadId}        at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
            });
        });
    }}

这给了我以下输出:

Console Output without await:
Before TaskContainingParallelForeach on thread: 1
Inside TaskContainingParallelForeach on thread: 3  // why is -below- first done all the work from the ParallelForeach? 
1 is running on thread: 3
3 is running on thread: 4
2 is running on thread: 6
1    stopped on thread: 3        at 1734365740995
2    stopped on thread: 6        at 1734365741996
3    stopped on thread: 4        at 1734365742996
After  TaskContainingParallelForeach on thread: 3 // why is it the thread with id 3 here - expected was 1
0. one second gone on thread:  3 at 1734365742996
1. one second gone on thread:  3 at 1734365743997
2. one second gone on thread:  3 at 1734365745008
3. one second gone on thread:  3 at 1734365746023
4. one second gone on thread:  3 at 1734365747035
5. one second gone on thread:  3 at 1734365748047
6. one second gone on thread:  3 at 1734365749061   

    

Parallel.Foreach 运行并且仅在其完成后 - while 循环开始。

await
运算符的行为应类似于: (原始文档)await 运算符暂停对封闭异步方法的评估,直到异步操作。我的异步方法是 TaskContainingParallelForeach。还是不是?

为什么

 After  TaskContainingParallelForeach on thread
的线程ID是3而不是1?为什么 while 循环没有在线程 1 上执行? 但是,从
await
中删除
await TaskContainingParallelForeach();
- 运算符可以获得所需的结果:

Console output without await:
Before TaskContainingParallelForeach on thread: 1
After  TaskContainingParallelForeach on thread: 1
Inside TaskContainingParallelForeach on thread: 3
1 is running on thread: 3
3 is running on thread: 4
2 is running on thread: 5
0. one second gone on thread:  1 at 1734364096009
1    stopped on thread: 3        at 1734364097021
1. one second gone on thread:  1 at 1734364097036
2    stopped on thread: 5        at 1734364098027
2. one second gone on thread:  1 at 1734364098043
3    stopped on thread: 4        at 1734364099022
3. one second gone on thread:  1 at 1734364099053
4. one second gone on thread:  1 at 1734364100064
5. one second gone on thread:  1 at 1734364101077
6. one second gone on thread:  1 at 1734364102078

这看起来棒极了!

  1. 所有工作 Parallel.Foreach 循环已启动
  2. 同时 while 循环开始工作
  3. while循环位于预期线程 1 上。

但这不是我认为

await
Task.Run()
Parallel.Foreach
应该一起使用的方式。我怎样才能改进这个代码?

提前致谢!

c# multithreading asynchronous async-await task-parallel-library
1个回答
0
投票

我想你想做的是这样的:

Console.WriteLine($"Before ParallelForeach on thread: {Environment.CurrentManagedThreadId}"); //Main debug 1
var parallelTask =  TaskContainingParallelForeach();
Console.WriteLine($"After  ParallelForeach on thread: {Environment.CurrentManagedThreadId}"); //Main debug 2
int counter = 0;
while (counter < 7) {
    Console.WriteLine($"{counter}. one second gone on thread:  {Environment.CurrentManagedThreadId} at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
    Thread.Sleep(1000);
    counter++;
}

await parallelTask; // want to join here
© www.soinside.com 2019 - 2024. All rights reserved.