sap*_*ito 10 .net c# multithreading asynchronous async-await
我有一个应该由两个进程更新的元素列表.第一个是UI线程(由用户控制),第二个是从Web服务检索信息的后台进程.
由于第二个进程受I/O限制,因此它似乎适用于异步任务.这引出了几个问题:
由于异步任务不在不同的线程上运行,因此在更新此列表时似乎不需要任何锁定,对吧?
另一方面,我们可以假设异步任务永远不会在不同的线程上运行吗?
我在谈论Windows窗体应用程序.也许在将来我希望它作为控制台应用程序运行它.AFAIK,在控制台应用程序中异步任务在不同的线程上运行.如果一个任务在一个单独的线程上运行,那么首选的成语是什么?这样我就可以在必要时建立锁定.
事实上,我不知道我是否真的需要一个锁,这让我想知道这是不是最好的设计.Task.Run()
即使对于这种IO绑定代码也坚持下去是否有意义?
由于异步任务不在不同的线程上运行,因此在更新此列表时似乎不需要任何锁定,对吧?
无法保证不使用Task.Run
和标记他的方法async
.IO绑定异步任务很可能不是在幕后使用某种线程,但情况并非总是如此.您不应该依赖于代码的正确性.您可以通过使用另一个async
不使用的方法包装代码来确保您的代码在UI线程上运行ConfigureAwait(false)
.您始终可以使用框架中提供的并发集合.也许你需要一个ConcurrentBag
或一个BlockingCollection
套房.
AFAIK,在控制台应用程序中异步任务在不同的线程上运行.如果一个任务在一个单独的线程上运行,那么首选的成语是什么?
那是不对的.async
由于他们在控制台应用程序中,他们自己的操作不会在单独的线程上运行.简单地说,TaskScheduler
控制台应用程序中的默认值是默认值ThreadPoolTaskScheduler
,它将在线程池线程上排队任何延续,因为控制台没有这样的实体称为ui线程.通常,它都是关于SynchronizationContext的
事实上,我不知道我是否真的需要一个锁,这让我想知道这是不是最好的设计.即使对于这种IO绑定代码,坚持使用Task.Run()是否有意义?
当然不.您不知道的事实是您发布此问题的原因,以及我们尝试提供帮助的原因.
没有必要使用线程池线程来执行异步IO.IO中异步的全部意义在于,您可以释放执行IO的调用线程,以便在处理请求时处理更多工作.
由于异步任务不在不同的线程上运行,因此在更新此列表时似乎不需要任何锁定,对吧?
真的够了.如果您遵循功能模式(即每个后台操作将返回其结果,而不是更新共享数据),则此方法很有效.所以,这样的事情会很好:
async Task BackgroundWorkAsync() // Called from UI thread
{
while (moreToProcess)
{
var item = await GetItemAsync();
Items.Add(item);
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,如何GetItemAsync
实现并不重要.它可以使用Task.Run
或者只需要ConfigureAwait(false)
它 - BackgroundWorkAsync
在将项目添加到集合之前始终与UI线程同步.
也许在将来我希望它作为控制台应用程序运行它.AFAIK,在控制台应用程序中异步任务在不同的线程上运行.
"异步任务"根本不运行.如果这令人困惑,我有一个可能有帮助的异步介绍.
每个异步方法都开始同步执行.当它命中时await
,它(默认情况下)捕获当前上下文,然后使用它来继续执行该方法.因此,从UI线程调用它时会发生的情况是该async
方法在捕获的UI上下文中恢复.控制台应用程序不提供上下文,因此该async
方法在线程池线程上恢复.
如果一个任务在一个单独的线程上运行,那么首选的成语是什么?这样我就可以在必要时建立锁定.
我推荐一种不会问这样的线程问题的设计.首先,你可以使用一个平原lock
- 当没有争用时它们非常快:
async Task BackgroundWorkAsync() // Called from any thread
{
while (moreToProcess)
{
var item = await GetItemAsync();
lock (_mutex)
Items.Add(item);
}
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以记录该组件依赖于提供的一次一个上下文,并使用类似AsyncContext
我的AsyncEx库中的控制台应用程序.