异步并等待-轮询替代方案


15

既然我们知道了c#5的存储内容,显然还有一个机会可以影响我们由Anders Heijsberg昨天在PDC10上宣布的两个“ 异步 ” 新关键字的选择。

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

埃里克·利珀特(Eric Lippert)对当前两个关键字的选择以及在可用性研究中被误解的方式进行了解释。这些评论还有其他一些主张。

请-每个答案一个建议,重复将被取消。


顺便说一下,“语言集成的异步编程”为我们提供了LIAP,与LINQ不太一样;)
Benjol 2010年

1
除非它有明显的飞跃。
康拉德·弗里克斯

3
但是“语言集成异步运行时”的缩写很好。
glenatron 2010年

这一定是题外话。
DeadMG 2012年

Answers:


6

鉴于我不清楚的含义/必要性async,因此我无法对此表示怀疑,但是我最好的替代建议await是:

yield while (看!没有新关键字)

请注意,对此进行了更多的考虑,我想知道while以这种方式重用是否是一个好主意-自然的趋势是以后会期望布尔值。

(认为​​:找到好的关键字就像找到好的域名:)


+1,顺便说一句,您击败我,在7分钟之前对他的博客条目发表了评论...
自我说明-想起一个名字,2010年

但是,如果任务已经完成,则不一定要执行。但是您总是在等待任务的完成(尽管从不等待)。
Allon Guralnek,2010年

@Allon while(x) {...}如果x为false ,则不必运行任何一个的循环体。
注意事项-想起

@注意:中没有动词while。如果您确实添加了一个动词(如)do,那么您会得到do {...} while (x),它会执行主体,而与x无关(至少一次)。您对的建议yield while似乎与相似do while,但对动词的保证相反,这可能会引起误解(但没什么大不了的)。我最不喜欢的事情yield是它暗示了一种机制的实现。async/ 的全部意思await是您以同步样式编写了一个异步操作。yield打破了同步风格。
Allon Guralnek,2010年

新关键字一定是一件坏事吗?据我了解,该await关键字将被上下文识别,因此如果您愿意,您仍然可以使用一个名为“ await”的方法或变量。在某种程度上,我认为使用新关键字来实现新功能要比重新使用现有关键字来含义更多的事情混淆。(夸张的示例:angerousmouse.net/esoteric/ook.html
Tim Goodman 2010年

5

没有关键字怎么办?

我希望编译器在大多数时候意识到当我调用异步方法时,我想要它的结果。

Document doc = DownloadDocumentAsync();

而已。人们很难为此事想一个关键词的原因是因为这就像有一个关键词,“如果事情完全正常,就要做你想做的事情”。那应该是默认值,不需要关键字。

更新资料

我最初建议编译器应该聪明地进行类型推断以弄清楚该怎么做。进一步考虑一下,我会将现有的实现保留在CTP中,但对其进行了一些琐碎的添加,以减少需要await显式使用关键字的情况。

我们发明了一个属性:[AutoAwait]。这只能应用于方法。将其应用于您的方法的一种方法是对其进行标记async。但是您也可以手动完成,例如:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

然后在任何async方法中,编译器将假定您要等待对的调用DownloadDocumentAsync,因此您无需指定它。对该方法的任何调用都会自动等待它。

Document doc = DownloadDocumentAsync();

现在,如果您想“变得聪明”并获得Task<Document>,则可以使用运算符start,该运算符只能出现在方法调用之前:

Task<Document> task = start DownloadDocumentAsync();

我想很整洁。现在,简单的方法调用意味着通常的含义:等待方法完成。并start表示不同的内容:请勿等待。

对于出现在async方法外部的代码,唯一允许您调用[AutoAwait]方法的方法是在其前面加上start。这迫使您编写具有相同含义的代码,而不管其是否出现在async方法中。

然后我开始变得贪婪!:)

首先,我要async应用于接口方法:

interface IThing
{
    async int GetCount();
} 

从根本上讲,这意味着实现方法必须返回Task<int>或与兼容的东西await,并且该方法的调用者将获得[AutoAwait]行为。

另外,当我实现上述方法时,我希望能够编写:

async int GetCount()

因此,我不必提及Task<int>返回类型。

另外,我想async将其应用于委托类型(毕竟,它们类似于具有一种方法的接口)。所以:

public async delegate TResult AsyncFunc<TResult>();

一位async代表-您猜对了- [AutoAwait]举止。从一个async方法中,您可以调用它,它将自动被await编辑(除非您选择仅使用start它)。因此,如果您说:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

那行得通。这不是方法调用。尚未启动任何任务-这async delegate不是任务。这是一个做任务的工厂。你可以说:

Document doc = getDoc();

这将启动任务,等待任务完成并提供结果。或者你可以说:

Task<Document> t = start getDoc();

因此,这里“管道”泄漏的一个地方是,如果您想委托一个async方法,则必须知道使用一种async delegate类型。因此,Func您不必说AsyncFunc,等等。尽管有一天,这种事情可能会通过改进的类型推断来解决。

另一个问题是,如果您说从普通(非异步)方法开始应该怎么办。显然,编译错误是安全的选择。但是还有其他可能性。


这对于隐式转换可能是可行的,但否则需要对语句进行从左到右的评估(除lambda之外,这与编译器正常工作的方式完全相反)。我认为我仍然会反对,因为它阻止了的使用var,可能不得不替换一些长的显式类型名称,并且在这种await情况和有人意外调用异步方法而不是正常的同步方法的情况之间也是模棱两可的。乍一看似乎很直观,但实际上违反了最小惊讶原则。
Aaronaught

@Aaronaught-为什么阻止使用var?我想知道您是否回应我的答案的先前版本...我已经完全重写了它。现在,您可以想到以下建议:如果该方法标记有特殊属性,就好像该await关键字自动插入到对该方法的调用之前一样(除非您用start前缀来抑制它)。一切都与CTP完全相同,因此var可以正常工作。
Daniel Earwicker 2010年

确实,我感到很奇怪,因为我决定重新访问该线程并几乎在您决定对其进行编辑的同时答复您的答案。我现在必须重新阅读...
Aaronaught

1
我喜欢您倒置await关键字。而且我也不喜欢的三重冗余public async Task<int> FooAsync()
Allon Guralnek,2010年

1
是的,我认为Async-postfix命名约定是可以更正式地捕获某些内容的标志。基本上,如果有一条规则说“应该以某种方式命名这样的方法,这样人们就知道如何正确地调用它们”,那么就可以得出结论,可以使用相同的规则以某种方式将这些方法归于这些方法。帮助您正确地给他们打电话。
Daniel Earwicker


4

我认为async很好,但这也许是因为我将其与ASP.NET异步页面关联-相同的想法。

对于await关键字,我更喜欢continue afterresume after

喜欢yield喜欢它的任何一种变体,因为语义使得该方法可能永远不会产生执行效果。这取决于任务的状态。


我喜欢resume afterawait。也许async可以称为resumable
Tim Goodman 2010年

尽管我只是喜欢after,但是该continue after方法具有强大的实现优势:它包含一个当前存在的上下文关键字,但是其语法与当前用法不兼容。这保证了添加不会破坏现有代码。当使用全新的关键字时,该实现需要处理单词在较旧代码上作为标识符的可能用途,这可能会非常棘手。
Edurne Pascual

3

我也在Eric的博客上添加了评论,但使用相同的关键字没有任何问题 async

var data = async DownloadFileAsync(url);

我只是在表达我要异步下载文件。这里有些冗余,“异步”出现了两次,因为它也在方法名称中。编译器可能更聪明,并且可以检测到以“ Async”结尾的方法实际上是异步方法,并在编译后的代码中为我们添加了约定。所以相反,您可能只想打电话

var data = async DownloadFile(url);

而不是称呼同步的

var data = DownloadFile(url);

哎呀,我们还应该能够以相同的方式定义它们,因为在声明中有async关键字,为什么我们必须在每个方法名称上手动添加“ Async”-编译器可以为我们做到这一点。


我喜欢您添加的糖,尽管C#家伙可能不会这么做。(FWIW,他们在搜索属性名称时已经做过类似的事情)
自我说明-想想一个名称,2010年

2
鉴于async该方法上的关键字只是一个不错的选择(如果我已经正确理解的话),我想知道最好的办法是否不是做与您建议相反的事情:放弃async方法,而只使用它他们目前拥有await
Benjol 2010年

这是可能的。实际上,为保证清洁,方法delcaration上的async关键字就在那里。我希望它保留在那里,但不需要我们在方法名称中添加“异步”。例如,async Task<Byte[]> DownloadFile(...)而不是Task<Byte[]> DownloadFileAsync(...)(反之,后者将始终是已编译的签名)。不管哪种方法。
Mark H

老实说,我也不喜欢这个。就像在前面的评论中一样,我必须指出您的最终版本违反了最小惊讶原则,因为它实际上是在调用与编写的方法完全不同的方法,并且该方法的实现和签名完全取决于实现类。 。甚至第一个版本也是有问题的,因为它实际上没有什么(异步执行此异步方法吗?)。我们试图表达的想法是继续执行推迟执行,但这根本没有表达出来。
Aaronaught

3

异步=任务 -它正在修改函数以返回任务,那么为什么不使用关键字“任务”呢?

await =完成 -我们不必等待,但是任务必须在使用结果之前“完成”。


这里的简单性真的很难争论。
2012年

2

我喜欢yield untilyield while,已经建议,很好,并且不会引入任何新的关键字,但是我认为“直到”可以更好地捕捉这种行为。

我认为这yield <something>是个好主意,因为yield已经捕获了将其余方法很好地延续的想法。也许有人会想到比“直到”更好的词。


2

我只想对Aaron G的建议进行投票,这comefrom是INTERCAL的COMEFROM语句的第一次适当使用。这个想法是它与GOTO相反( GOTO语句跳转),因为它在您的代码中有一些地方跳转 COMEFROM语句。


2

由于我们正在处理Task<T>s,因此如何start在语句之前将其用作关键字,如:

start var document = FetchAsync(urls[i]);


嗯,也许finish会比start
主角

1

值得注意的是,F#async在其异步工作流程中也使用了关键字,这几乎与C#5中的新异步功能完全相同。因此,我会保持不变

对于awaitF#中的关键字,它们仅使用let!代替let。C#没有相同的赋值语法,因此它们在=符号的右侧需要一些东西。正如Benjol所说,它的功能与之相同,yield因此几乎应该是它的变体。


1
“等待”根本不需要在赋值中(尽管通常如此)。作为几乎所有可以在其上找到GetAwaiter的类型的表达式的运算符,这都是合法的。(确切的规则尚未制定成可发布的形式。)
Eric Lippert 2010年

1
@Eric,在F#中应该是do!,但是您知道……
Benjol 2010年

1

yield async FetchAsync(..)


这与async需要在调用方法上放置的修饰符完美匹配。还有当前的语义,yield return即返回产生对枚举代码的执行,而在这种情况下,您将产生对异步方法的执行。

想象一下,如果将来还有其他用途yield,我们可以添加一个yield xx作为闪亮的新功能,而不用使用所有这些不同的关键字来完成大部分相同的事情,从而产生执行力。

坦白说,我不太理解“不屈服执行”的论点。毕竟,调用另一个方法的目的不是已经使该方法“屈服执行”了吗?不管它是否异步?我在这里想念什么吗?

如果async返回的结果是同步的,但是在其中带有关键字,则对您有好处,这表明该方法有可能异步运行,并且您将让另一个方法执行。无论方法实际上是否进行异步调用,您的方法都应考虑到这一点。

IMO,我认为各种“不屈服”的情况都是实施细节。我宁愿保证语言的一致性(即重用yield)。


0

怎么样complete,如“我要完成任务”中所示?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;

1
为什么要下票?至少在投票后解释一下自己是有礼貌的。
Allon Guralnek,2010年

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.