在C#5.0中模糊异步和常规函数之间的界限


10

最近,我似乎对C#5.0 的惊人异步等待模式不够了解。我这辈子去哪了

我对简单的语法感到非常兴奋,但是我遇到了一个小困难。我的问题是异步函数的声明与常规函数完全不同。由于只有异步函数可以等待其他异步函数,因此当我尝试将一些旧的阻止代码移植到异步时,我产生了必须转换的函数的多米诺骨牌效应。

人们一直称其为僵尸大批出没。当异步在您的代码中被咬时,它将不断变得越来越大。移植过程并不困难,只需将其async放入声明中并用来包装返回值Task<>。但是在移植旧的同步代码时,一遍又一遍地执行此操作很烦人。

在我看来,如果两种函数类型(异步和普通旧同步)具有完全相同的语法,那将是自然得多的。如果是这种情况,移植将花费零的精力,我可以在两种形式之间轻松切换。

我认为如果遵循以下规则,这可能会起作用:

  1. 异步函数不再需要async声明。他们的返回类型不必包装Task<>。编译器将在编译期间自行识别异步函数,并根据需要自动执行Task <>包装。

  2. 不再需要对异步函数进行“一劳永逸”的调用。如果要调用异步函数,则需要等待它。无论如何,我几乎都不用一劳永逸,疯狂的比赛条件或僵局的所有例子似乎总是基于它们。我认为他们与我们试图利用的同步思维过于混乱和“脱节”。

  3. 如果您真的不能一劳永逸,那将有特殊的语法。无论如何,它不会成为我正在谈论的简单统一语法的一部分。

  4. 您需要表示异步调用的唯一关键字是await。如果您正在等待,则该调用是异步的。如果您不这样做,则该呼叫是普通的老式同步呼叫(请记住,我们不再具有即兴即弃的功能)。

  5. 编译器将自动识别异步函数(因为它们不再有特殊的声明了)。规则4使得此操作非常简单-如果函数await内部有调用,则它是异步的。

能行吗?还是我错过了什么?这种统一的语法更加流畅,可以完全解决僵尸的侵扰。

一些例子:

// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }

// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }

// now let's try all combinations and see what they do:

// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();

// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();

// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();

// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();

注意:这是SO中有趣的相关讨论的延续


3
您总是在等待异步操作吗?请告诉我,解雇他们后不要立即这样做……
Jimmy Hoffa 2013年

1
另外,异步的一大优点是您不必await立即进行操作。你可以做类似的事情var task = FooAsync(); Bar(); await task;。在您的提案中我将如何处理?
svick

3
SO正在讨论吗? 我的BFG-3000呢?
罗伯特·哈维

2
@talkol您认为并行编程是淫秽的?至少可以说,这是一个有趣的观点async。我认为这是的一大优势async- await:它可以让您轻松撰写异步操作(而不仅仅是简单的“启动,等待,启动B,等待B”的方式)。并且已经有专门用于此目的的特殊语法:称为await
2013年

1
@svick哈哈,现在我们去了:)我不认为并行编程是淫秽的,但是我认为使用async-await做到这一点。异步等待是一种语法糖,用于保持您的同步心态而无需付出阻塞的代价。如果您已经在并行考虑,我敦促您使用其他模式
talkol

Answers:


9

您所链接的SO问题中已经回答了您的问题。

异步/等待的目的是使在具有许多高延迟操作的环境中更容易编写代码。绝大多数的操作都是高延迟。

WinRT首次问世时,设计人员正在描述他们如何决定哪些操作将是异步的。他们认为将花费50毫秒或更长时间的任何事物都是异步的,而其余方法将是普通的非异步方法。

为了使它们异步,必须重写多少方法?其中约百分之十。其他90%则完全没有受到影响。

埃里克·利珀特(Eric Lippert)继续以相当可观的技术细节来解释为什么他们选择不采用“一刀切”的方法。他基本上说,asyncawait是部分实现的延续传递风格,并且优化这样的风格适合所有的情况下是一个难题。


请注意SO问题与这一问题之间的实质区别。SO询问为什么不使所有内容异步。这里我们不建议这样做,我们建议使10%异步,仅使用相同的语法即可。使用更紧密的语法的优势在于,您可以更轻松地更改异步的10%,而不会受到这些更改的多米诺骨牌影响
talkol 2013年

我不清楚为什么async会产生僵尸大批出没。即使某个方法调用了其他10种方法,您是否也不必async使用顶级方法?
罗伯特·哈维

6
假设我当前代码的100%是同步的。现在,我有一个内部的叶级函数,用于查询要更改为异步的数据库。现在要使其异步,我需要它的调用者是异步的,它的调用者是异步的,依此类推,直到顶层。当然,我说的是等待整个链(保持代码的同步设计或传递返回值)的情况
talkol
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.