线程安全类是否应该在其构造函数末尾有一个内存屏障?

2024-02-08

当实现一个线程安全的类时,我是否应该在其构造函数末尾包含一个内存屏障,以确保任何内部结构在可以访问之前已完成初始化?或者消费者有责任在使实例可供其他线程使用之前插入内存屏障?

简化问题:

下面的代码中是否存在竞争危险,由于初始化和线程安全类的访问之间缺乏内存屏障,可能会导致错误的行为?或者线程安全类本身应该防止这种情况发生?

ConcurrentQueue<int> queue = null;

Parallel.Invoke(
    () => queue = new ConcurrentQueue<int>(),
    () => queue?.Enqueue(5));

请注意,程序不将任何内容放入队列是可以接受的,如果第二个委托在第一个委托之前执行,就会发生这种情况。 (空条件运算符?.防止NullReferenceException此处。)但是,程序抛出一个错误是不可接受的。IndexOutOfRangeException, NullReferenceException, 入队5多次,陷入无限循环,或者做任何其他由内部结构的竞争危险引起的奇怪的事情。

详细问题:

具体来说,想象一下我正在为队列实现一个简单的线程安全包装器。 (我知道.NET已经提供了ConcurrentQueue<T> https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx;这只是一个例子。)我可以写:

public class ThreadSafeQueue<T>
{
    private readonly Queue<T> _queue;

    public ThreadSafeQueue()
    {
        _queue = new Queue<T>();

        // Thread.MemoryBarrier(); // Is this line required?
    }

    public void Enqueue(T item)
    {
        lock (_queue)
        {
            _queue.Enqueue(item);
        }
    }

    public bool TryDequeue(out T item)
    {
        lock (_queue)
        {
            if (_queue.Count == 0)
            {
                item = default(T);
                return false;
            }

            item = _queue.Dequeue();
            return true;
        }
    }
}

一旦初始化,此实现就是线程安全的。但是,如果初始化本身由另一个消费者线程进行竞争,则可能会出现竞争危险,即后一个线程将在内部线程之前访问实例。Queue<T>已初始化。作为一个人为的例子:

ThreadSafeQueue<int> queue = null;

Parallel.For(0, 10000, i =>
{
    if (i == 0)
        queue = new ThreadSafeQueue<int>();
    else if (i % 2 == 0)
        queue?.Enqueue(i);
    else
    {
        int item = -1;
        if (queue?.TryDequeue(out item) == true)
            Console.WriteLine(item);
    }
});

上面的代码遗漏一些数字是可以接受的;然而,如果没有内存障碍,它也可能会得到一个NullReferenceException(或其他一些奇怪的结果)由于内部Queue<T>尚未初始化Enqueue or TryDequeue叫做。

线程安全类有责任在其构造函数末尾包含内存屏障,还是使用者应该在类的实例化与其对其他线程的可见性之间包含内存屏障? .NET Framework 中标记为线程安全的类的约定是什么?

Edit:这是一个高级线程主题,所以我理解一些评论中的混乱。一个实例can如果从其他线程访问而没有正确的同步,则显示为半生不熟。本主题在双重检查锁定的上下文中进行了广泛讨论,在不使用内存屏障(例如通过volatile). Per 乔恩·斯基特 http://csharpindepth.com/Articles/General/Singleton.aspx#dcl:

Java 内存模型不确保构造函数在对新对象的引用分配给实例之前完成。 Java 内存模型在 1.5 版本中进行了重新设计,但在此之后,如果没有 易失性变量,双重检查锁定仍然会被破坏(as in C#).

没有任何内存障碍,它在 ECMA CLI 规范中也被破坏了。在 .NET 2.0 内存模型(比 ECMA 规范更强)下它可能是安全的,但我不想依赖那些更强的语义,特别是如果对安全性有任何疑问的话。


Lazy<T>是线程安全初始化的一个非常好的选择。我认为应该由消费者来提供:

var queue = new Lazy<ThreadSafeQueue<int>>(() => new ThreadSafeQueue<int>());

Parallel.For(0, 10000, i =>
{

    else if (i % 2 == 0)
        queue.Value.Enqueue(i);
    else
    {
        int item = -1;
        if (queue.Value.TryDequeue(out item) == true)
            Console.WriteLine(item);
    }
});
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程安全类是否应该在其构造函数末尾有一个内存屏障? 的相关文章

随机推荐

  • 在弹出窗口中显示部分视图[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我是 MVC 框架的新手 我需要您的
  • 如何在知道线程 id 的情况下获取消息线程 URL?

    有如果我有消息 ID 如何构建链接以查看 facebook com 上的消息 http facebook stackoverflow com questions 7747622 how can i construct a link to v
  • jquery mobile 和 ui 不兼容

    尽管有很多人提到类似的兼容性问题 但 50 的问题在 StackOverflow 上得到了解决 我希望我的问题能够成为 51 49 考虑这段代码
  • macOS 公证:找不到 altool

    我想开始构建一个公证自动化脚本 但是 当我尝试在终端中使用 xcrun altool 时 出现以下错误 xcrun error unable to find utility altool not a developer tool or in
  • 如何正确引用本地XML Schema文件?

    我在 XML 文件中引用 XML 架构时遇到此问题 我的 XSD 位于此路径中 C environment workspace maven ws ProjectXmlSchema email xsd 但是 当我在 XML 文件中尝试像这样查
  • 服务器标记格式不正确

    这真是太愚蠢了 但却让我快疯了
  • 堆叠 UITableViews 不会在其视图下方传递触摸事件

    我将 3 个 UIView 堆叠在一起 UI表格视图平面视图根视图 TableView 位于顶部 rootView 位于底部 rootView 不可见 因为 TableView 在它上面 我在 rootView 中实现了以下代码 code
  • 错误 TS2707 通用类型“ɵɵDirectiveDeclaration”需要 6 到 8 个类型参数

    安装角度材料并将角度材料导入 app module ts 添加到项目后 我遇到错误 并且到目前为止所有解决方案都不起作用 我的角度为 14 节点为 16 第一个错误 实际上要长得多 Error node modules angular cd
  • 如何使用 Python 从巨大的 Excel 工作表中提取特定行的数据?

    我需要获取其中包含某些关键字 名称 的特定数据行并将它们写入另一个文件 起始文件是 1 5 GB Excel 文件 我不能只是打开它并将其另存为不同的格式 我应该如何使用 python 处理这个问题 我是 xlrd 的作者和维护者 请编辑您
  • 如何提高Python循环速度?

    我有一个包含 370k 记录的数据集 存储在 Pandas Dataframe 中 需要集成 我尝试了多处理 线程 Cpython 和循环展开 但我没有成功 显示的计算时间是 22 小时 任务如下 matplotlib inline fro
  • 开发游戏服务器用什么语言好?

    我只是想知道什么语言是开发支持大量 数千 用户的游戏服务器的不错选择 我涉足Python 但意识到这太麻烦了 因为它不会跨核心产生线程 意味着8个核心服务器 1个核心服务器 我也不太喜欢这种语言 自我 的东西让我感到恶心 我知道 C 就性能
  • 在xamarin forms pcl项目中打开远程pdf的最佳方法

    在适用于 Ios 和 Android 的 xamarin pcl 应用程序中 在服务器上加载 pdf 的最佳方式是什么 是否有一个好的 nuget 或者我们必须编写自定义渲染器 在应用程序中打开 PDF 您有几个选项 iOS 在其 WebV
  • 使用 Cython 将 Python 链接到共享库

    我正在尝试集成用以下语言编写的第三方库C和我的python应用程序使用Cython 我已经为测试编写了所有 python 代码 我无法找到设置此功能的示例 我有一个pyd pyx我手动创建的文件 第三方给了我一个header file h
  • 使用Delphi RTTI获取接口的字符串名称

    我已经证明我可以使用 Delphi 2010 从其 GUID 获取接口的名称 例如 IMyInterface 转换为字符串 IMyInterface 我想在 Delphi 7 中实现此目的 为了兼容性 这可能吗 或者是存在基本的编译器限制
  • 哪种数据结构最适合 VirtualStringTree?

    我想每个曾经使用过Delphi的VirtualStringTree的人都会同意它是一个很棒的控件 它是一个 虚拟 控件 您的数据必须保存在其他地方 所以我在想什么数据结构最适合这样的任务 IMO认为数据结构必须支持层次结构 它必须快速且易于
  • 扩展器的默认控制模板

    有人 可能使用 Blend 可以为我提供 WPF Expander 的工作默认 ControlTemplate 吗 我想做一些细微的修改 但似乎找不到有效模板的来源 提前致谢 我有混合 可以帮助你 这是 Blend 为我生成的内容
  • 根据日期分割数据框

    我正在尝试根据日期将数据框分成两个 此处的相关问题已解决 根据日期将数据帧分成两部分 https stackoverflow com questions 37532098 split dataframe into two on the ba
  • Chrome 语音合成具有较长的文本

    我在 Chrome 33 中尝试使用语音合成 API 时遇到问题 它可以完美地处理较短的文本 但如果我尝试较长的文本 它就会停在中间 一旦停止后 语音合成将无法在 Chrome 中的任何地方工作 直到浏览器重新启动 示例代码 http js
  • 责任链模式是否可以很好地替代一系列条件?

    当您需要按特定顺序执行一系列操作时 责任链模式是否可以很好地替代一系列条件 用这样的条件替换简单的方法是个好主意吗 public class MyListener implements MyHttpListener if false the
  • 线程安全类是否应该在其构造函数末尾有一个内存屏障?

    当实现一个线程安全的类时 我是否应该在其构造函数末尾包含一个内存屏障 以确保任何内部结构在可以访问之前已完成初始化 或者消费者有责任在使实例可供其他线程使用之前插入内存屏障 简化问题 下面的代码中是否存在竞争危险 由于初始化和线程安全类的访