使用异步/等待时是否需要使字段成为线程安全的? [英] Is it needed to make fields thread-safe when using async/await?

查看:149
本文介绍了使用异步/等待时是否需要使字段成为线程安全的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有时,我会遇到访问对象字段的异步/等待代码.例如,该代码段来自无状态项目的代码:

Sometimes I encounter async/await code that accesses fields of an object. For example this snippet of code from the Stateless project:

private readonly Queue<QueuedTrigger> _eventQueue = new Queue<QueuedTrigger>();
private bool _firing;

async Task InternalFireQueuedAsync(TTrigger trigger, params object[] args)
{
    if (_firing)
    {
        _eventQueue.Enqueue(new QueuedTrigger { Trigger = trigger, Args = args });
        return;
    }

    try
    {
        _firing = true;

        await InternalFireOneAsync(trigger, args).ConfigureAwait(false);

        while (_eventQueue.Count != 0)
        {
            var queuedEvent = _eventQueue.Dequeue();
            await InternalFireOneAsync(queuedEvent.Trigger, queuedEvent.Args).ConfigureAwait(false);
        }
    }
    finally
    {
        _firing = false;
    }
}

如果我正确理解 await **.ConfigureAwait(false)表示在此 await 之后执行的代码确实

If I understand correctly the await **.ConfigureAwait(false) indicates that the code that is executed after this await does not necessarily has to be executed on the same context. So the while loop here could be executed on a ThreadPool thread. I don't see what is making sure that the _firing and _eventQueue fields are synchronized, for example what is creating the a lock/memory-fence/barrier here? So my question is; do I need to make the fields thread-safe, or is something in the async/await structure taking care of this?

以澄清我的问题;在这种情况下,应始终在同一线程上调用 InternalFireQueuedAsync .在那种情况下,只有延续可以在不同的线程上运行,这让我感到奇怪,我是否需要同步机制(例如显式屏障)来确保值被同步以避免出现此处描述的问题:

to clarify my question; in this case InternalFireQueuedAsync should always be called on the same thread. In that case only the continuation could run on a different thread, which makes me wonder, do I need synchronization-mechanisms(like an explicit barrier) to make sure the values are synchronized to avoid the issue described here: http://www.albahari.com/threading/part4.aspx

在无状态下也有一个小讨论: https://github.com/dotnet-state-machine/stateless/issues/294

Edit 2: there is also a small discussion at stateless: https://github.com/dotnet-state-machine/stateless/issues/294

推荐答案

我看不到是什么确保_firing和_eventQueue字段是同步的,例如,是什么在这里创建了lock/memory-fence/barrier?所以我的问题是;我需要使字段成为线程安全的,还是async/await结构中的某些东西来解决这个问题?

I don't see what is making sure that the _firing and _eventQueue fields are synchronized, for example what is creating the a lock/memory-fence/barrier here? So my question is; do I need to make the fields thread-safe, or is something in the async/await structure taking care of this?

await 将确保所有必要的内存屏障均已到位.但是,这并不能使它们具有线程安全性".

await will ensure all necessary memory barriers are in place. However, that doesn't make them "thread-safe".

在这种情况下,应始终在同一线程上调用InternalFireQueuedAsync.

in this case InternalFireQueuedAsync should always be called on the same thread.

然后 _firing 很好,不需要 volatile 或类似的东西.

Then _firing is fine, and doesn't need volatile or anything like that.

但是, _eventQueue 的用法不正确.考虑当线程池线程在 await 之后恢复代码时会发生什么:完全有可能 Queue< T> .Count Queue< T>.Dequeue()将由线程池线程同时调用 Queue< T> .Enqueue 由主线程调用.这不是线程安全的.

However, the usage of _eventQueue is incorrect. Consider what happens when a thread pool thread has resumed the code after the await: it is entirely possible that Queue<T>.Count or Queue<T>.Dequeue() will be called by a thread pool thread at the same time Queue<T>.Enqueue is called by the main thread. This is not threadsafe.

如果调用 InternalFireQueuedAsync 的主线程是具有单线程上下文的线程(例如UI线程),那么一个简单的解决方法是删除 ConfigureAwait(false).

If the main thread calling InternalFireQueuedAsync is a thread with a single-threaded context (such as a UI thread), then one simple fix is to remove all the instances of ConfigureAwait(false) in this method.

这篇关于使用异步/等待时是否需要使字段成为线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆