jjzjj

c# - 如何保证 Array 中 "reference type"项目的更新对其他线程可见?

coder 2024-06-03 原文

private InstrumentInfo[] instrumentInfos = new InstrumentInfo[Constants.MAX_INSTRUMENTS_NUMBER_IN_SYSTEM];

public void SetInstrumentInfo(Instrument instrument, InstrumentInfo info)
{
    if (instrument == null || info == null)
    {
        return;
    }
    instrumentInfos[instrument.Id] = info;  // need to make it visible to other threads!
}

public InstrumentInfo GetInstrumentInfo(Instrument instrument)
{
    return instrumentInfos[instrument.Id];  // need to obtain fresh value!
}

SetInstrumentInfoGetInstrumentInfo 从不同线程调用。 InstrumentInfo 是不可变类。 调用 GetInstrumentInfo 时,我是否保证拥有最新的副本?恐怕我会收到“缓存”副本。我应该添加某种同步吗?

instrumentInfos 声明为 volatile 无济于事,因为我需要将数组项声明为 volatile,而不是数组本身。

我的代码有问题吗?如果有,如何解决?

UPD1:

我需要我的代码在现实生活中工作而不是符合所有规范!因此,如果我的代码在现实生活中有效,但在某些环境下的某些计算机上“理论上”无法正常工作 - 没关系!

  • 我需要我的代码在 Windows 下使用最新的 .NET Framework 在现代 X64 服务器(目前有 2 个处理器 HP DL360p Gen8)上运行。
  • 我不需要在陌生的计算机或 Mono 或其他任何东西下工作我的代码
  • 我不想引入延迟,因为这是 HFT 软件。因此,“微软的实现使用强大的内存模型进行写入。这意味着写入被视为 volatile ”,我可能不需要添加额外的 Thread.MemoryBarrier ,它只会增加延迟.我认为我们可以相信微软将在未来的版本中继续使用“强内存模型”。至少微软不太可能改变内存模型。所以我们假设它不会。

UPD2:

最近的建议是使用 Thread.MemoryBarrier();。现在我不明白我必须将它插入的确切位置才能使我的程序在标准配置(x64、Windows、Microsoft .NET 4.0)上运行。请记住,我不想插入“只是为了能够在 IA64 或 .NET 10.0 上启动您的程序”的行。速度对我来说比便携性更重要。然而,如何更新我的代码以便它可以在任何计算机上运行也很有趣。

UPD3

.NET 4.5 解决方案:

    public void SetInstrumentInfo(Instrument instrument, InstrumentInfo info)
    {
        if (instrument == null || info == null)
        {
            return;
        }
        Volatile.Write(ref instrumentInfos[instrument.Id], info);
    }

    public InstrumentInfo GetInstrumentInfo(Instrument instrument)
    {
        InstrumentInfo result = Volatile.Read(ref instrumentInfos[instrument.Id]);
        return result;
    }

最佳答案

这个问题的答案又长又复杂,但我会尝试将其提炼成一些可行的建议。

<强>1。简单的解决方案:仅在锁定下访问 instrumentInfos

避免多线程程序中不可预测性的最简单方法是始终使用锁来保护共享状态。

根据您的意见,您似乎认为此解决方案过于昂贵。您可能想要仔细检查该假设,但如果情况确实如此,那么让我们看看剩余的选项。

<强>2。进阶方案:Thread.MemoryBarrier

或者,您可以使用 Thread.MemoryBarrier:

private InstrumentInfo[] instrumentInfos = new InstrumentInfo[Constants.MAX_INSTRUMENTS_NUMBER_IN_SYSTEM]; 

public void SetInstrumentInfo(Instrument instrument, InstrumentInfo info) 
{ 
    if (instrument == null || info == null) 
    { 
        return; 
    } 

    Thread.MemoryBarrier(); // Prevents an earlier write from getting reordered with the write below

    instrumentInfos[instrument.Id] = info;  // need to make it visible to other threads! 
} 

public InstrumentInfo GetInstrumentInfo(Instrument instrument) 
{ 
    InstrumentInfo info = instrumentInfos[instrument.Id];  // need to obtain fresh value! 
    Thread.MemoryBarrier(); // Prevents a later read from getting reordered with the read above
    return info;
}

在写入之前和读取之后使用 Thread.MemoryBarrier 可以防止潜在的麻烦。第一个内存屏障防止写线程用将对象发布到数组中的写操作对初始化对象字段的写操作重新排序,第二个内存屏障防止读线程对从数组接收对象的读操作重新排序。随后读取该对象的字段。

作为旁注,.NET 4 还公开了使用 Thread.MemoryBarrier 的 Thread.VolatileRead 和 Thread.VolatileWrite,如上所示。但是,对于 System.Object 以外的引用类型,没有 Thread.VolatileRead 和 Thread.VolatileWrite 的重载。

<强>3。高级解决方案 (.NET 4.5):Volatile.Read 和 Volatile.Write

.NET 4.5 公开了比完整内存屏障更有效的 Volatile.Read 和 Volatile.Write 方法。如果您的目标是 .NET 4,此选项将无济于事。

<强>4。 “错误但会发生”解决方案

你永远不应该依赖我将要说的话。但是……您不太可能重现原始代码中存在的问题。

事实上,在 .NET 4 中的 X64 上,如果您能重现它,我会感到非常惊讶。 X86-X64 提供了相当强大的内存重新排序保证,因此这些类型的 Release模式恰好可以正常工作。 .NET 4 C# 编译器和 .NET 4 CLR JIT 编译器也避免了会破坏您的模式的优化。因此,允许重新排序内存操作的三个组件都不会碰巧这样做。

就是说,有一些(有点模糊的) Release模式变体实际上不适用于 X64 中的 .NET 4。因此,即使您认为代码永远不需要在 .NET 4 X64 以外的任何体系结构上运行,如果您使用其中一种正确的方法,您的代码将更易于维护,即使该问题目前无法在您的服务器上重现.

关于c# - 如何保证 Array 中 "reference type"项目的更新对其他线程可见?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11954589/

有关c# - 如何保证 Array 中 "reference type"项目的更新对其他线程可见?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  7. ruby - 在 Ruby 中实现 `call_user_func_array` - 2

    我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  10. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

随机推荐