-2

我正在启动 2 个任务,但没有await-ing 它们,其中一个取决于另一个。我试图理解为什么以下代码阻塞被截断。

public class Tasks {
        EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset);

        public async Task Job1() {
            Console.WriteLine("Finished job1");
            handle.Set();
        }

        public async Task Job2() {
            handle.WaitOne();
            Console.WriteLine("Doing Job2 work");
        }
    }

    class Program {
        static async Task Main(string[] args) {

                Tasks seq = new Tasks();
                var t2 =seq.Job2();
                var t1 =seq.Job1();

                await Task.WhenAll(t1, t2);
                Console.WriteLine("finished both");
        }
    }

如果我CPU-bound为我的两个任务创建任务它可以工作:

var t2=Task.Run(seq.Job2);
var t1=Task.Run(seq.Job1);

我还尝试将这两个任务放在与主线程不同的任务中,但它仍然会阻塞:

var bigtask=Task.Run(async()=>{
                         var t2 =seq.Job2();
                         var t1 =seq.Job1();
                     });

CPU-bound如果我在没有等待的情况下启动一项任务,这与开始一项新任务几乎没有什么不同吗?( Task.Run)

4

2 回答 2

2

查看您的编译器警告;他们会告诉你到底出了什么问题。具体来说,您使用asyncwithout await,因此这些方法将同步运行。

Task.Run在线程池线程上执行该方法,这会阻止它同步运行。

如果我在没有等待的情况下启动任务,它与 [使用 Task.Run] 几乎不一样吗?

每个async方法都开始同步执行await是它可以异步运行的点。

async本身不使用任何线程(或线程池);它更像是一种更高级的回调语法。Task.Run确实使用线程池。


要解决您的潜在问题(让一个任务等待另一个任务),最简单的方法是将Task返回的 from传递Job1给该Job2方法,然后Job2 await执行该任务。如果这不可能,那么您需要一种异步信号(而不是阻塞信号EventWaitHandle)。一次性异步信号是TaskCompletionSource<T>SemaphoreSlim还支持异步等待;和更复杂的协调原语是我的AsyncEx 库的一部分。

于 2019-04-10T12:48:19.950 回答
1

声明方法“异步”不会自动使您的代码多线程。您可以假设您的代码将同步运行,直到“等待”某些内容。

这里的问题是 Job2 永远不会返回,所以你的代码会卡住。但是,例如(而不是实际的解决方案),如果你做了这样的事情:

            public async Task Job2()
            {
                await Task.Delay(1000);
                handle.WaitOne();
                Console.WriteLine("Doing Job2 work");
            }

您的程序实际上会退出,因为该函数将变为异步并在等待延迟后返回给调用者。

在 TPL(async/await) 中通常应避免使用诸如“EventWaitHandle/ManualResetEvent”之类的同步原语,因为它们会物理阻塞线程,而不是释放线程并等待回调。

这是一个实际的解决方案:

    public class Tasks
    {
        SemaphoreSlim semaphore = new SemaphoreSlim(0);

        public async Task Job1()
        {
            Console.WriteLine("Finished job1");
            semaphore.Release();
        }

        public async Task Job2()
        {
            await semaphore.WaitAsync();
            Console.WriteLine("Doing Job2 work");
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {

            Tasks seq = new Tasks();
            var t2 = seq.Job2();
            var t1 = seq.Job1();

            await Task.WhenAll(t1, t2);
            Console.WriteLine("finished both");
        }
    }
于 2019-04-11T18:03:20.670 回答