首页 \ 问答 \ VC ++仍然按顺序 - 一致性明智吗?(Is VC++ still broken Sequentially-Consistent-wise?)

VC ++仍然按顺序 - 一致性明智吗?(Is VC++ still broken Sequentially-Consistent-wise?)

我观察了(大部分) Herb Sutter的atmoic <>武器视频 ,我想用样本内的循环测试“条件锁定”。 显然,尽管(如果我理解正确的话)C ++ 11标准说下面的例子应该正常工作并且顺序一致,但事实并非如此。

在你阅读之前,我的问题是:这是正确的吗? 编译器是否损坏? 我的代码是否被破解 - 我在这里遇到了一个我错过的竞争条件? 我如何绕过这个?

我尝试过3种不同版本的Visual C ++:VC10 professional,VC11 professional和VC12 Express(== Visual Studio 2013 Desktop Express)。

下面是我用于Visual Studio 2013的代码。对于其他版本,我使用boost而不是std,但是这个想法是一样的。

#include <iostream>
#include <thread>
#include <mutex>

int a = 0;
std::mutex m;

void other()
{
    std::lock_guard<std::mutex> l(m);
    std::this_thread::sleep_for(std::chrono::milliseconds(2));
    a = 999999;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << a << "\n";
}

int main(int argc, char* argv[])
{
    bool work = (argc > 1);

    if (work)
    {
        m.lock();
    }

    std::thread th(other);
    for (int i = 0; i < 100000000; ++i)
    {
        if (i % 7 == 3)
        {
            if (work)
            {
                ++a;
            }
        }
    }

    if (work)
    {
        std::cout << a << "\n";
        m.unlock();
    }

    th.join();
}

总结代码的思想:全局变量a受全局互斥量m保护。 假设没有命令行参数( argc==1 ),则运行other()的线程是唯一应该访问全局变量a的线程。

该程序的正确输出是打印999999。

但是,由于编译器循环优化(使用寄存器循环增量,并且在循环结束时将值复制回a ),所以尽管不应该这样,但程序会修改a

这发生在所有3个VC版本中,尽管在VC12中的这个代码示例中,我不得不调用一些调用sleep()来使其中断。

这是一些汇编代码(在这次运行中的地址是0x00f65498 ):

循环初始化 - 来自a值被复制到edi

    27:     for (int i = 0; i < 100000000; ++i)
00F61543  xor         esi,esi  
00F61545  mov         edi,dword ptr ds:[0F65498h]  
00F6154B  jmp         main+0C0h (0F61550h)  
00F6154D  lea         ecx,[ecx]  
    28:     {
    29:         if (i % 7 == 3)

在条件内增加,并在循环之后无条件复制回位置

    30:         {
    31:             if (work)
00F61572  mov         al,byte ptr [esp+1Bh]  
00F61576  jne         main+0EDh (0F6157Dh)  
00F61578  test        al,al  
00F6157A  je          main+0EDh (0F6157Dh)  
    32:             {
    33:                 ++a;
00F6157C  inc         edi  
    27:     for (int i = 0; i < 100000000; ++i)
00F6157D  inc         esi  
00F6157E  cmp         esi,5F5E100h  
00F61584  jl          main+0C0h (0F61550h)  
    32:             {
    33:                 ++a;
00F61586  mov         dword ptr ds:[0F65498h],edi  
    34:             }

程序的输出是0


I watched (most of) Herb Sutter's the atmoic<> weapons video, and I wanted to test the "conditional lock" with a loop inside sample. Apparently, although (if I understand correctly) the C++11 standard says the below example should work properly and be sequentially consistent, it is not.

Before you read on, my question is: Is this correct? Is the compiler broken? Is my code broken - do I have a race condition here which I missed? How do I bypass this?

I tried it on 3 different versions of Visual C++: VC10 professional, VC11 professional and VC12 Express (== Visual Studio 2013 Desktop Express).

Below is the code I used for the Visual Studio 2013. For the other versions I used boost instead of std, but the idea is the same.

#include <iostream>
#include <thread>
#include <mutex>

int a = 0;
std::mutex m;

void other()
{
    std::lock_guard<std::mutex> l(m);
    std::this_thread::sleep_for(std::chrono::milliseconds(2));
    a = 999999;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << a << "\n";
}

int main(int argc, char* argv[])
{
    bool work = (argc > 1);

    if (work)
    {
        m.lock();
    }

    std::thread th(other);
    for (int i = 0; i < 100000000; ++i)
    {
        if (i % 7 == 3)
        {
            if (work)
            {
                ++a;
            }
        }
    }

    if (work)
    {
        std::cout << a << "\n";
        m.unlock();
    }

    th.join();
}

To summarize the idea of the code: The global variable a is protected by the global mutex m. Assuming there are no command line arguments (argc==1) the thread which runs other() is the only one which is supposed to access the global variable a.

The correct output of the program is to print 999999.

However, because of the compiler loop optimization (using a register for in-loop increments and at the end of the loop copying the value back to a), a is modified by the assembly even though it's not supposed to.

This happened in all 3 VC versions, although in this code example in VC12 I had to plant some calls to sleep() to make it break.

Here's some of the assembly code (the adress of a in this run is 0x00f65498):

Loop initialization - value from a is copied into edi

    27:     for (int i = 0; i < 100000000; ++i)
00F61543  xor         esi,esi  
00F61545  mov         edi,dword ptr ds:[0F65498h]  
00F6154B  jmp         main+0C0h (0F61550h)  
00F6154D  lea         ecx,[ecx]  
    28:     {
    29:         if (i % 7 == 3)

Increment within the condition, and after the loop copied back to the location of a unconditionally

    30:         {
    31:             if (work)
00F61572  mov         al,byte ptr [esp+1Bh]  
00F61576  jne         main+0EDh (0F6157Dh)  
00F61578  test        al,al  
00F6157A  je          main+0EDh (0F6157Dh)  
    32:             {
    33:                 ++a;
00F6157C  inc         edi  
    27:     for (int i = 0; i < 100000000; ++i)
00F6157D  inc         esi  
00F6157E  cmp         esi,5F5E100h  
00F61584  jl          main+0C0h (0F61550h)  
    32:             {
    33:                 ++a;
00F61586  mov         dword ptr ds:[0F65498h],edi  
    34:             }

And the output of the program is 0.


原文:https://stackoverflow.com/questions/24313168
更新时间:2024-01-09 15:01

最满意答案

在冒号后添加一个空格:

Manifest-Version: 1.0
Main-Class: t

Add a space after the colons:

Manifest-Version: 1.0
Main-Class: t

相关问答

更多
  • 在冒号后添加一个空格: Manifest-Version: 1.0 Main-Class: t Add a space after the colons: Manifest-Version: 1.0 Main-Class: t
  • 你看过这个工具OneJar吗? 这也是阅读OneJar的好文章。 如果您决定不使用外部工具,那么您可以选择将库/助手罐放入清单中的classpath中,然后将罐子本身复制到相对于主jar的目录路径中。 编辑:OP问一个例子MANIFEST.MF。 我从示例One-Jar示例 jar文件处理了这个问题。 Manifest-Version: 1.0 Main-Class: com.simontuffs.onejar.Boot One-Jar-Expand: expand,doc Have you looked ...
  • 我刚刚通过编辑我的Entitlements.plist解决了确切的问题。我发现如何/为什么在这篇文章 。 您可以将您的Entitlements.plist作为属性文件进行编辑(请参阅Pranav的答案),也可以像我一样直接编辑它。 我的Entitlements.plist现在类似于: -
  • 回答我自己的问题.. :) 取决于应用程序,但应用程序可以使用的最大内存为16MB。 只需在写入文件之前添加一个检查(我希望有一个更聪明的方式) Answering my own question.. :) Depends on the application but the maximum memory an application can use is 16MB. Simply by adding a check before writing to the file (I was hoping for ...
  • 该错误表示类路径上的JAR文件之一包含MANIFEST.MF,其中包含com / microsoft / sqlserver / jdbc / SQLServerException.class的条目,并且其中的SHA1校验和不匹配。 首先要检查的是JAR没有损坏。 既然你可以从Eclipse内部运行项目,他们似乎没问题。 这导致了导出的JAR。 我的猜测是出口过程在那里留下垃圾。 打开它(JAR压缩文件是ZIP压缩文件,因此任何ZIP工具都可以工作)并查找MANIFEST.MF文件。 其中之一包含上面提到的 ...
  • 如果可以,请切换到使用CocoaPods。 更新您的版本标头搜索路径以包含"$(SRCROOT)/Build/Headers"和"$(BUILD_ROOT)/../" 。 Problem solved! the cause is that my Build Settings > Architecture is not set properly. Here's what i did: App submission: arm64 and armv7 Simulator: armv7 Device: arm64
  • Directory类有一个方法来接收其中的所有文件的列表。 该方法允许您指定过滤字符串,如下所示: Directory.GetFiles(directoryPath, filterString); 如果您已经知道了文件名前缀,则可以使用该过滤器字符串来获取该模式中的所有文件: filterString = string.Format("{0}_*.{1}", defaultFileNameWithoutExtension, defaultExtension); 然后,您只需选择后缀最高的那个,提取后缀数 ...
  • 生成的清单文件似乎包含一些与预期的“key-colon-value”语法不匹配的行(包含独立$ {BUILD_NUMBER}的行,也可能是前面的空行),这可能导致解析错误在堆栈跟踪中 The generated manifest file seems to contain some lines that don't match the expected "key-colon-value" syntax (the line containing standalone ${BUILD_NUMBER}, and ...
  • FWIW我也遇到过这个问题,这就是我发现的。 报告的字节0x62,0x70,0x6c等是二进制属性列表开头的魔术字符串“bplist”的一部分,NSKeyedArchiver默认使用该列表。 二进制属性列表在预告片中(即在数据的末尾)存储元数据。 因此,如果它被截断,整个plist变得不可读。 如果您想检查是否发生了这种情况,可以使用Cocotron的NSPropertyListReader_binary1( http://code.google.com/p/cocotron/source/browse/F ...
  • 我必须说你应该将工件版本作为非prod和prod部署的标准流程。 通常在非prod环境中你可以规划SNAPSHOT版本,在生产中你应该选择RELEASE版本,可以使用maven-release-plugin使用mvn release prepare release perform 。 它将提升您的pom版本以用于下一个后续版本。 您可以将工件存储到AWS S3或Artifactory或Nexus(用于高可用性),就像您在此处引用的ubuntu机器一样。 现在我建议你再添加一个名为stage('Release ...

相关文章

更多

最新问答

更多
  • 获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)
  • 如何通过引用返回对象?(How is returning an object by reference possible?)
  • 矩阵如何存储在内存中?(How are matrices stored in memory?)
  • 每个请求的Java新会话?(Java New Session For Each Request?)
  • css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)
  • 无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)
  • xcode语法颜色编码解释?(xcode syntax color coding explained?)
  • 在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)
  • 从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)
  • 从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))
  • 西安哪有PLC可控制编程的培训
  • 在Entity Framework中选择基类(Select base class in Entity Framework)
  • 在Android中出现错误“数据集和渲染器应该不为null,并且应该具有相同数量的系列”(Error “Dataset and renderer should be not null and should have the same number of series” in Android)
  • 电脑二级VF有什么用
  • Datamapper Ruby如何添加Hook方法(Datamapper Ruby How to add Hook Method)
  • 金华英语角.
  • 手机软件如何制作
  • 用于Android webview中图像保存的上下文菜单(Context Menu for Image Saving in an Android webview)
  • 注意:未定义的偏移量:PHP(Notice: Undefined offset: PHP)
  • 如何读R中的大数据集[复制](How to read large dataset in R [duplicate])
  • Unity 5 Heighmap与地形宽度/地形长度的分辨率关系?(Unity 5 Heighmap Resolution relationship to terrain width / terrain length?)
  • 如何通知PipedOutputStream线程写入最后一个字节的PipedInputStream线程?(How to notify PipedInputStream thread that PipedOutputStream thread has written last byte?)
  • python的访问器方法有哪些
  • DeviceNetworkInformation:哪个是哪个?(DeviceNetworkInformation: Which is which?)
  • 在Ruby中对组合进行排序(Sorting a combination in Ruby)
  • 网站开发的流程?
  • 使用Zend Framework 2中的JOIN sql检索数据(Retrieve data using JOIN sql in Zend Framework 2)
  • 条带格式类型格式模式编号无法正常工作(Stripes format type format pattern number not working properly)
  • 透明度错误IE11(Transparency bug IE11)
  • linux的基本操作命令。。。