多核CPU上32位读取的原子性 [英] Atomicity of 32bit read on multicore CPU

查看:123
本文介绍了多核CPU上32位读取的原子性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(注意:我已根据可能会帮助您的人们的意愿在此问题上添加标签,因此请不要大喊:))

(Note: I've added tags to this question based on where I feel will people will be who are likely to be able to help, so please don't shout:))

在我的VS 2017 64位项目中,我有一个32位长值m_lClosed.当我想更新它时,我使用了Interlocked系列功能之一.

In my VS 2017 64bit project, I have a 32bit long value m_lClosed. When I want to update this, I use one of the Interlocked family of functions.

考虑以下代码,在线程#1上执行

Consider this code, executing on thread #1

LONG lRet = InterlockedCompareExchange(&m_lClosed, 1, 0);   // Set m_lClosed to 1 provided it's currently 0

现在考虑下面的代码,在线程#2上执行:

Now consider this code, executing on thread #2:

if (m_lClosed) // Do something

我知道在单个CPU上,这不会有问题,因为更新是原子的,读取也是原子的(请参阅

I understand that on a single CPU, this will not be a problem because the update is atomic and the read is atomic too (see MSDN), so thread pre-emption cannot leave the variable in a partially updated state. But on a multicore CPU, we really could have both these pieces of code executing in parallel if each thread is on a different CPU. In this example, I don't think that would be a problem, but it still feels wrong to be testing something that is in the process of possibly being updated.

此网页告诉我,多个CPU的原子性是通过LOCK汇编指令实现的,阻止其他CPU访问该内存.这听起来像我所需要的,但是为上面的if测试生成的汇编语言仅仅是

This webpage tells me that atomicity on multiple CPUs is achieved via the LOCK assembly instruction, preventing other CPUs from accessing that memory. That sounds like what I need, but the assembly language generated for the if test above is merely

cmp   dword ptr [l],0  

...看不到LOCK指令.

我们应该如何在这种情况下确保读取的原子性?

修改18年4月24日

首先,感谢您对此问题的浓厚兴趣.我在下面显示实际代码;我故意将重点放在所有内容的原子性上保持简单,但是显然,如果我从第一分钟开始就展示了所有内容,那会更好.

Firstly thanks for all the interest this question has generated. I show below the actual code; I purposely kept it simple to focus on the atomicity of it all, but clearly it would have been better if I had showed it all from minute one.

第二,实际代码所在的项目是VS2005项目; 因此无法访问C ++ 11原子.这就是为什么我没有在问题中添加C ++ 11标记的原因.我正在将VS2017与临时"项目一起使用,以节省每次学习时进行更改时都不必构建大型VS2005的麻烦.另外,它是一个更好的IDE.

Secondly, the project in which the actual code lives is a VS2005 project; hence no access to C++11 atomics. That's why I didn't add the C++11 tag to the question. I am using VS2017 with a "scratch" project to save having to build the huge VS2005 one every time I make a change whilst I am learning. Plus, its a better IDE.

是的,因此实际的代码位于IOCP驱动的服务器中,而这整个原子性是关于处理封闭的套接字的:

Right, so the actual code lives in an IOCP driven server, and this whole atomicity is about handling a closed socket:

class CConnection
{
    //...

    DWORD PostWSARecv()
    {
        if (!m_lClosed)
            return ::WSARecv(...);
        else
            return WSAESHUTDOWN;
    }

    bool SetClosed()
    {
        LONG lRet = InterlockedCompareExchange(&m_lClosed, 1, 0);   // Set m_lClosed to 1 provided it's currently 0
        // If the swap was carried out, the return value is the old value of m_lClosed, which should be 0.
        return lRet == 0;
    }

    SOCKET m_sock;
    LONG m_lClosed;
};

呼叫者将呼叫SetClosed();如果返回true,则将调用::closesocket()等.请不要质疑为什么会这样,它只是:)

The caller will call SetClosed(); if it returns true, it will then call ::closesocket() etc. Please don't question why it is that way, it just is :)

考虑一个线程关闭套接字而另一个线程尝试发布WSARecv()时会发生什么.您可能会认为WSARecv()将会失败(毕竟套接字已关闭!);但是,如果使用与我们刚刚关闭的套接字相同的套接字句柄建立了新连接,该怎么办-然后我们将发布WSARecv(),它将成功,但这对我的程序逻辑来说是致命的,因为我们现在将一个完全不同的连接与此CConnection对象相关联.因此,我有if (!m_lClosed)测试.您可能会争辩说,我不应该在多个线程中处理相同的连接,但这不是这个问题的重点 :)

Consider what happens if one thread closes the socket whilst another tries to post a WSARecv(). You might think that the WSARecv() will fail (the socket is closed after all!); however what if a new connection is established with the same socket handle as that which we just closed - we would then be posting the WSARecv() which will succeed, but this would be fatal for my program logic since we are now associating a completely different connection with this CConnection object. Hence, I have the if (!m_lClosed) test. You could argue that I shouldn't be handling the same connection in multiple threads, but that is not the point of this question :)

这就是为什么在进行WSARecv()调用之前需要测试m_lClosed的原因.

That is why I need to test m_lClosed before I make the WSARecv() call.

现在,很明显,我只是将m_lClosed设置为1,所以读/写操作撕裂并不是一个真正的问题,但是这是我关注的原则.如果将m_lClosed设置为2147483647然后测试2147483647怎么办?在这种情况下,读/写操作将很麻烦.

Now, clearly, I am only setting m_lClosed to a 1, so a torn read/write is not really a concern, but it is the principle I am concerned about. What if I set m_lClosed to 2147483647 and then test for 2147483647? In this case, a torn read/write would be more problematic.

推荐答案

确定,因此事实证明这确实没有必要; 答案详细说明了为什么我们不需要为简单的读/写操作而使用任何互锁的操作(但我们确实进行读取-修改-写入).

OK so as it turns out this really isn't necessary; this answer explains in detail why we don't need to use any interlocked operations for a simple read/write (but we do for a read-modify-write).

这篇关于多核CPU上32位读取的原子性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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