C# 如何以及何时从可等待(异步)方法获取返回

C# 如何以及何时从可等待(异步)方法获取返回,c#,.net,async-await,C#,.net,Async Await,我很确定,这个问题已经得到了回答,我读了很多相关的东西,但不知何故,我没有让它在我的代码中工作。下面是确切的代码库 我的库中有一个异步方法,它在执行某些DB条目时返回一个字符串: public class MyLibrary { public async Task<string> DoSomethingAsync() { return await DoAsync(); } // some private method, with multiple parameters p

我很确定,这个问题已经得到了回答,我读了很多相关的东西,但不知何故,我没有让它在我的代码中工作。下面是确切的代码库

我的库中有一个异步方法,它在执行某些DB条目时返回一个字符串:

public class MyLibrary
{
public async Task<string> DoSomethingAsync()
{
    return await DoAsync();
} 

// some private method, with multiple parameters
private Task<string> DoAsync()
{
    return Task.Run(() => Do());
}
}
因为我的调用是可等待的,我想,UI线程会等待我的调用完成其业务并填充结果,不是吗?或者,由于调用正在另一个线程上运行,当光标指向第2行时,任务可能尚未完成。那现在呢?每当任务完成时,光标将等待任务完成或执行第2行?对我来说是同步的

此外,如果我真的想“合法地”等到任务完成,我该如何执行?一些帖子,建议像下面这样做,这给了我结果,但造成了更多的混乱:

var myTask = Task.Run(async () => await MyLibraryobject.DoSomethingAsync());
myTask.Wait();
console.Write(myTask.Result);
上面发生了什么?为什么要创建另一个任务并将其设置为等待?我不能等待异步方法使用的线程吗?再一次,我显然遗漏了一些基本的东西

最后,这是一个异步调用,但UI线程正在等待它完成,因此给我的印象是它是同步的。我想对了吗?那么,异步方法的主要用途是什么,它返回一些东西,UI等待它完成

另外,fire-and-forget调用是非异步的,与异步的有什么区别?对于这样的调用,返回任务而不是void有什么好处吗

我很确定,在我的大脑中,有一个断开的链接将所有这些片段连接在一起。有人能给我解释一下吗

现在在UI上,以下结果导致冻结状态或死锁:

var myTask = MyLibraryobject.DoSomethingAsync();
console.Write(myTask.Result); 
这会阻塞UI,因为它没有等待。您只需要等待一个异步方法

string res = await MyLibraryobject.DoSomethingAsync();
上述内容也应该放在异步上下文中的UI中


编辑-回复注释“您是指另一个包装器异步方法,它返回
任务
…”?不,我没有。当我写“在异步上下文中”时,我的意思是将async关键字添加到订阅的事件中,如按钮单击,如果您在代码隐藏中,或者添加到委托命令实现中,如果您在GUI的更高级MVVM实现中

此外,您的类库应该做一些真正异步的事情,而不仅仅是启动一个任务并将其包装到一个正式的异步方法中,就像它是您的
DoAsync


编辑-回复评论“应该如何避免”吗?如果不能始终执行异步,只需保持库和API的同步,并通过启动单独的线程调用同步API来解除对UI的阻止

您可能应该从我的开始,然后再看我关于的文章。他们几乎回答了你所有的问题

有关详细信息

如何以及何时从可等待(异步)方法获取返回

您应该使用
wait
从异步任务中获取结果

我的库中有一个异步方法,它在执行一些DB条目时返回一个字符串

然后它不应该使用
任务。运行
。它应该使用自然异步API,例如实体框架的
FirstAsync

因为我的调用是可等待的,我想,UI线程会等待我的调用完成其业务并填充结果,不是吗

否。由于您的代码正在调用
Result
,因此UI被阻止<代码>结果
是一个阻塞调用

对我来说是同步的

这是因为您使用
Result
使其同步。如果使用
wait
,代码将以串行方式但异步方式执行

此外,如果我真的想“合法地”等到任务完成,我该如何执行

您的代码已经在这样做了。在任务完成之前,它是显式阻塞的。如果您将其更改为正确使用
wait
,它将显式等待(但不阻塞),直到任务完成

一些帖子,建议在下面这样做

否。不要使用
任务。不必要地运行

我不能等待异步方法使用的线程吗

纯异步方法没有它们使用的线程

那么,异步方法的主要用途是什么,它返回一些东西,UI等待它完成


Async在使用
wait

时是有意义的。当您调用Async方法时,您不能保证内容将在与调用方法不同的线程中运行。这就是为什么有时您应该将其包装在
Task.Run
(或
Task.Factory.StartNew
)中。另外,当调用
myTask.Result
时,
myTask.Wait()
是隐式的。为了避免阻塞线程UI,您应该将事件处理程序方法标记为async。您是指另一个包装器异步方法,它返回任务,调用类似于,
return wait MyLibraryobject.DoSomethingAsync()
?另外,我不明白在我的库方法中还应该做什么?好的。我在一个API方法中调用这个库方法,这就是我没有在这里直接使用'wait'关键字的原因,因为这会迫使我向API方法添加'async'关键字。所以我创建了一个包装器,它只返回正在等待的任务,比如
return await MyLibraryobject.DoSomethingAsync()
。但这并不奏效。根据您的评论,我在API方法中添加了'async',并使用wait获得了您刚才提到的结果。我的困惑是,为什么我在包装中做了同样的“等待”时,它不起作用。创建一个包装器是否引用了另一个任务?另外,我知道我不应该公开一个异步的对应项,它只是将同步方法包装在task.Run中。这正是我用我的库方法所做的;它以前是同步的,我通过包装在Task.Run中使它异步。我应该如何避免这种做法并仍然使用异步版本?@如果无法创建异步版本,请跳过
string res = await MyLibraryobject.DoSomethingAsync();