异步任务和锁

sap*_*ito 10 .net c# multithreading asynchronous async-await

我有一个应该由两个进程更新的元素列表.第一个是UI线程(由用户控制),第二个是从Web服务检索信息的后台进程.

由于第二个进程受I/O限制,因此它似乎适用于异步任务.这引出了几个问题:

  1. 由于异步任务不在不同的线程上运行,因此在更新此列表时似乎不需要任何锁定,对吧?

  2. 另一方面,我们可以假设异步任务永远不会在不同的线程上运行吗?

  3. 我在谈论Windows窗体应用程序.也许在将来我希望它作为控制台应用程序运行它.AFAIK,在控制台应用程序中异步任务在不同的线程上运行.如果一个任务在一个单独的线程上运行,那么首选的成语是什么?这样我就可以在必要时建立锁定.

  4. 事实上,我不知道我是否真的需要一个锁,这让我想知道这是不是最好的设计.Task.Run()即使对于这种IO绑定代码也坚持下去是否有意义?

Yuv*_*kov 8

由于异步任务不在不同的线程上运行,因此在更新此列表时似乎不需要任何锁定,对吧?

无法保证不使用Task.Run和标记他的方法async.IO绑定异步任务很可能不是在幕后使用某种线程,但情况并非总是如此.您不应该依赖于代码的正确性.您可以通过使用另一个async不使用的方法包装代码来确保您的代码在UI线程上运行ConfigureAwait(false).您始终可以使用框架中提供的并发集合.也许你需要一个ConcurrentBag或一个BlockingCollection套房.

AFAIK,在控制台应用程序中异步任务在不同的线程上运行.如果一个任务在一个单独的线程上运行,那么首选的成语是什么?

那是不对的.async由于他们在控制台应用程序中,他们自己的操作不会在单独的线程上运行.简单地说,TaskScheduler控制台应用程序中的默认值是默认值ThreadPoolTaskScheduler,它将在线程池线程上排队任何延续,因为控制台没有这样的实体称为ui线程.通常,它都是关于SynchronizationContext的

事实上,我不知道我是否真的需要一个锁,这让我想知道这是不是最好的设计.即使对于这种IO绑定代码,坚持使用Task.Run()是否有意义?

当然不.您不知道的事实是您发布此问题的原因,以及我们尝试提供帮助的原因.

没有必要使用线程池线程来执行异步IO.IO中异步的全部意义在于,您可以释放执行IO的调用线程,以便在处理请求时处理更多工作.

  • 我不同意并发和集合的观点.我同意不保证您在UI线程上运行.但是,由于数据绑定,最好只访问UI线程中的集合.这表明`async` I/O应该是纯的(因此是线程安全的),并且`Presenter/Controller/whatever`应该是线程不安全的,并且要求回调在UI线程上运行.但是关于`锁定'."异步"编程的目的是使编写单线程阻塞(锁定)免费程序变得更容易. (2认同)

Ste*_*ary 5

由于异步任务不在不同的线程上运行,因此在更新此列表时似乎不需要任何锁定,对吧?

真的够了.如果您遵循功能模式(即每个后台操作将返回其结果,而不是更新共享数据),则此方法很有效.所以,这样的事情会很好:

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库中的控制台应用程序.