等待任务不等待 [英] await Task does not wait

查看:115
本文介绍了等待任务不等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的通用节气门功能的MS文档实现形式.

I have a simple taken form MS documentation implementation of generic throttle function.

 public async Task RunTasks(List<Task> actions, int maxThreads)
    {
        var queue = new ConcurrentQueue<Task>(actions);
        var tasks = new List<Task>();
        for (int n = 0; n < maxThreads; n++)
        {
            tasks.Add(Task.Run(async () =>
            {
                while (queue.TryDequeue(out Task action))
                {
                    action.Start();
                    await action;
                    int i = 9; //this should not be reached.
                }
            }));
        }
        await Task.WhenAll(tasks);
    }

要测试它,我有一个单元测试:

To test it I have a unit test:

 [Fact]
    public async Task TaskRunningLogicThrottles1()
    {
        var tasks = new List<Task>();
        const int limit = 2;

        for (int i = 0; i < 2000; i++)
        {
            var task = new Task(async () => {
                await Task.Delay(-1);
            });
            tasks.Add(task);
        }
        await _logic.RunTasks(tasks, limit);
    }

由于任务中存在Delay(-1),因此该测试永远都无法完成.行"int y = 9;"表示行.在RunTasks函数中应该永远都不能达到.但是,它确实起作用了,而我的整个功能却无法执行应做的工作-节流执行.如果相反,还是等待Task.Delay(),我使用了同步Thread.Sleep,则可以正常工作.

Since there is a Delay(-1) in the tasks this test should never complete. The line "int y = 9;" in the RunTasks function should never be reached. However it does and my whole function fails to do what it is supposed to do - throttle execution. If instead or await Task.Delay() I used synchronous Thread.Sleep ot works as exptected.

推荐答案

Task 类没有代码无效 .这是一个常见的陷阱.仅仅因为编译器允许我们向任何lambda添加 async 关键字,并不意味着我们应该这样做.我们应该仅将异步lambda传递给期望并理解它们的方法,这意味着参数的类型应为

The Task class has no constructor that accepts async delegates, so the async delegate you passed to it is async void. This is a common trap. Just because the compiler allows us to add the async keyword to any lambda, doesn't mean that we should. We should only pass async lambdas to methods that expect and understand them, meaning that the type of the parameter should be a Func with a Task return value. For example Func<Task>, or Func<Task<T>>, or Func<TSource, Task<TResult>> etc.

您可以 要做的是将异步lambda传递给 Task< TResult> 构造函数,在这种情况下,将解析 TResult 作为 Task .换句话说,您可以创建嵌套的 Task< Task> 实例:

What you could do is to pass the async lambda to a Task<TResult> constructor, in which case the TResult would be resolved as Task. In other words you could create nested Task<Task> instances:

var taskTask = new Task<Task>(async () =>
{
    await Task.Delay(-1);
});

这样,您将有一个很酷的外部任务,即在启动时将创建内部任务.创建任务所需的工作可以忽略不计,因此将立即创建内部任务.内部任务将是一个Promise样式的任务,就像异步方法生成的所有任务一样.承诺式任务在创建时总是很热门.不能像基于委托的任务那样在冷状态下创建它.调用其 Start 方法会导致 InvalidOperationException .

This way you would have a cold outer task, that when started would create the inner task. The work required to create a task is negligible, so the inner task will be created instantly. The inner task would be a promise-style task, like all tasks generated by async methods. A promise-style task is always hot on creation. It cannot be created in a cold state like a delegate-based task. Calling its Start method results to an InvalidOperationException.

创建冷任务和嵌套任务是一种高级技术,在实践中很少使用.按需启动promise样式任务的常用技术是将它们作为异步委托传递(各种口味的 Func< Task> 实例),然后在适当的时候调用每个委托.

Creating cold tasks and nested tasks is an advanced technique that is used rarely in practice. The common technique for starting promise-style tasks on demand is to pass them around as async delegates (Func<Task> instances in various flavors), and invoke each delegate at the right moment.

这篇关于等待任务不等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆