我不太明白Task和Task的区别。等啊等。

我有类似于以下功能在一个ASP。NET WebAPI服务:

public class TestController : ApiController
{
    public static async Task<string> Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        return "";
    }

    public async static Task<string> Bar()
    {
        return await Foo();
    }

    public async static Task<string> Ros()
    {
        return await Bar();
    }

    // GET api/test
    public IEnumerable<string> Get()
    {
        Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());

        return new string[] { "value1", "value2" }; // This will never execute
    }
}

Get将死锁的位置。

是什么导致了这种情况?为什么当我使用阻塞等待而不是await Task.Delay时,这不会引起问题?


等待和等待——虽然概念相似——实际上完全不同。

Wait将同步阻塞直到任务完成。因此,当前线程被阻塞,等待任务完成。一般来说,你应该自始至终使用“async”;也就是说,不要阻塞异步代码。在我的博客上,我详细介绍了异步代码中的阻塞是如何导致死锁的。

Await将异步等待直到任务完成。这意味着当前方法处于“暂停”状态(它的状态被捕获),并且该方法将未完成的任务返回给调用者。之后,当await表达式完成时,该方法的其余部分被调度为一个延续。

你还提到了“协作块”,我想你的意思是你正在等待的任务可能会在等待线程上执行。有些情况下会发生这种情况,但这是一种优化。在许多情况下,它不会发生,比如任务是另一个调度器的任务,或者它已经启动,或者它是非代码任务(例如在您的代码示例中:Wait不能内联执行Delay任务,因为没有用于它的代码)。

你可能会发现我的async / await介绍很有用。


根据我从不同来源读到的:

await表达式不会阻塞它正在执行的线程。相反,它会导致编译器将async方法的其余部分注册为等待任务的延续。控件然后返回到异步方法的调用方。当任务完成时,它调用它的延续,异步方法的执行从它停止的地方恢复。

要等待单个任务完成,可以调用它的task。等方法。对Wait方法的调用将阻塞调用线程,直到单个类实例完成执行。无参数Wait()方法用于无条件地等待任务完成。任务通过调用线程来模拟工作。睡眠法:先睡两秒钟。

这篇文章也很值得一读。


一些重要的事实在其他答案中没有给出:

async/await在CIL级别上更复杂,因此会消耗内存和CPU时间。

如果等待时间不可接受,任何任务都可以取消。

在async/await的情况下,我们没有这样一个任务的处理程序来取消它或监视它。

使用Task比async/await更灵活。

任何同步功能都可以通过async封装。

public async Task<ActionResult> DoAsync(long id) 
{ 
    return await Task.Run(() => { return DoSync(id); } ); 
} 

Async /await会产生很多问题。我们不知道在没有运行时和上下文调试的情况下是否会到达await语句。如果第一个等待没有到达,一切都被阻塞。有时候,等待似乎到了,但一切仍然受阻:

https://github.com/dotnet/runtime/issues/36063

我不明白为什么我必须生活在代码复制同步和异步方法或使用黑客。

结论:手动创建任务并控制它们要好得多。Handler to Task提供了更多的控制。我们可以监视任务并管理它们:

https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem

对不起,我的英语不好。