Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ConfigureWait并混合异步和同步调用_C#_Multithreading_Asynchronous_Async Await - Fatal编程技术网

C# ConfigureWait并混合异步和同步调用

C# ConfigureWait并混合异步和同步调用,c#,multithreading,asynchronous,async-await,C#,Multithreading,Asynchronous,Async Await,我已经阅读了很多关于异步/等待编程模型的文章,但仍有一些事情不太清楚,我想与大家分享我对这些问题的困惑 假设我们有以下配置: 主要的异步方法是public async Task DoSomethingBigAsync(){…},它在内部有3个其他方法,如下所示: A) var A=await\u someInstance.DoSomethingLittle\u A\u Async().ConfigureAwait(false) B) var B=wait _someInstance.DoSome

我已经阅读了很多关于异步/等待编程模型的文章,但仍有一些事情不太清楚,我想与大家分享我对这些问题的困惑

假设我们有以下配置:

主要的异步方法是
public async Task DoSomethingBigAsync(){…}
,它在内部有3个其他方法,如下所示:

A)
var A=await\u someInstance.DoSomethingLittle\u A\u Async().ConfigureAwait(false)
B)
var B=wait _someInstance.DoSomethingLittle_B_Async()
C)
var C=\u someInstance.DoSomethingLittle\u C()

主方法是从UI线程调用的。我们知道上下文将被捕获,当主方法完成时,上下文将被恢复

如果我没有记错的话,当调用第一个(A)异步方法时,由于
ConfigureAwait(false)
的原因,UI上下文不会被捕获,而是将延续从池中推送到线程。但这并不一定会发生,因为通话可能会立即完成。出于这个原因,我怀疑应该对所有内部异步方法调用
ConfigureAwait(false)
AB)。这是否正确,或者运行时在看到对
ConfigureAwait(false)
的第一个调用后知道该做什么

我的另一个担忧是两个坏习惯(或被认为是坏习惯)及其副作用

根据Stephen Toub的文章,一种不好的做法似乎是:

公开库中同步方法的异步包装器 不好

因此,创建
DoSomethingLittle\u C()
方法的异步版本不是一个好主意

另一个坏习惯似乎是:

不要混合使用块代码和异步代码

现在,看看上面的代码,如果第一个
ConfigureAwait(false)
可以保证将continuation推送到线程池,那么使用
Task.StartNew(()=>{c=\u someInstance.DoSomethingLittle\u c()})不会有任何附加值。
另一方面,如果
ConfigureAwait(false)
从不保证将continuation推送到线程池,那么我们可能会遇到问题,我们必须确保使用
Task.StartNew(()=>{c=\u someInstance.DoSomethingLittle\u c();})

错误(?):
A)
var A=await\u someInstance.DoSomethingLittle\u A\u Async().ConfigureAwait(false)
B)
var B=wait _someInstance.DoSomethingLittle_B_Async()
C)
var C=\u someInstance.DoSomethingLittle\u C()

正确(?):
A)
var A=await\u someInstance.DoSomethingLittle\u A\u Async().ConfigureAwait(false)
B)
var B=await\u someInstance.DoSomethingLittle\u B\u Async().ConfigureAwait(false)
C)
var C=Task.StartNew(()=>{return\u someInstance.DoSomethingLittle\u C()})

“因此,我怀疑应该对所有内部异步方法(本例中为A和B)调用ConfigureAwait(false)。这是否正确,或者运行时在看到对ConfigureAwait(false)的第一次调用后知道该怎么做?”

对。应该这样。因为,正如你所说的,上一次通话可能会同步完成,也因为上一次通话可能会在将来更改,你不想依赖它

关于
Task.Run
(这比
Task.Factory.StartNew更可取),问题是是否在API的实现中使用它,答案几乎总是否定的

你的情况不同。如果您在UI线程上,并且有大量的工作(Stephen Cleary建议超过50毫秒)可以在后台完成,那么将这些工作转移到
线程池
线程以保持UI的响应性没有什么错。就像
ConfigureAwait
一样,我不会依赖前面的调用将您移动到
ThreadPool
,因为它也可以同步完成

因此,正确:

var a = await _someInstance.DoSomethingLittle_A_Async().ConfigureAwait(false);
var b = await _someInstance.DoSomethingLittle_B_Async().ConfigureAwait(false);
var c = await Task.Run(() => _someInstance.DoSomethingLittle_C()).ConfigureAwait(false);

是的,当然,如果他们能有意义地升级到
DoSomethingLittle\u CAsync()
,那就更好了。我还要补充一点,您应该只使用
Task.Run,如果需要的话。一般的经验法则是,只有当
DoSomethingLittle\u C
需要超过50ms时,才应该在这里使用它。(另外,您缺少最后一个
等待
)。如果您在Task.Factory.StartNew或Task.Run中执行程序,那么程序终止情况如何?你能保证任务在终止前完成吗?我正在寻找与旧的Thread.IsBackground功能类似的功能。更准确地说,是否有一种简单的方法可以在调用链中自动执行至少一些代码片段(例如:更新数据库或确保文件写入完成)?@PincoPallino这取决于您的应用程序类型。但没有真正的保证。应用程序可能会崩溃或者机器可能会断电,这些操作没有任何原子性。