线程同步101 [英] Thread Synchronisation 101

查看:141
本文介绍了线程同步101的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以前,我写了一些非常简单的多线程代码,我一直知道,在任何时候都可能有一个上下文切换正在我做的中间,所以我一直保护访问共享变量通过CCriticalSection类进入构建的关键部分,并将其释放。我知道这是相当积极的,我进入和离开关键部分相当频繁,有时甚至异常(例如,在一个函数的开始,当我可以把CCriticalSection在一个更严格的代码块),但我的代码不会崩溃,它运行足够快。

Previously I've written some very simple multithreaded code, and I've always been aware that at any time there could be a context switch right in the middle of what I'm doing, so I've always guarded access the shared variables through a CCriticalSection class that enters the critical section on construction and leaves it on destruction. I know this is fairly aggressive and I enter and leave critical sections quite frequently and sometimes egregiously (e.g. at the start of a function when I could put the CCriticalSection inside a tighter code block) but my code doesn't crash and it runs fast enough.

在工作中,我的多线程代码需要更紧凑,只需要锁定/同步所需的最低级别。

At work my multithreaded code needs to be a tighter, only locking/synchronising at the lowest level needed.

在工作中,我试图调试一些多线程代码,我遇到了这个:

At work I was trying to debug some multithreaded code, and I came across this:

EnterCriticalSection(&m_Crit4);
m_bSomeVariable = true;
LeaveCriticalSection(&m_Crit4);

现在, m_bSomeVariable 是一个Win32 BOOL不挥发),据我所知,被定义为一个int,并且在x86读取和写入这些值是一个单一的指令,并且由于上下文切换发生在指令边界,所以不需要同步这个操作与临界

Now, m_bSomeVariable is a Win32 BOOL (not volatile), which as far as I know is defined to be an int, and on x86 reading and writing these values is a single instruction, and since context switches occur on an instruction boundary then there's no need for synchronising this operation with a critical section.

我在网上做了一些研究,看看这个操作是否不需要同步,我想出了两个方案:

I did some more research online to see whether this operation did not need synchronisation, and I came up with two scenarios it did:


  1. CPU执行无序执行或第二个线程正在不同的内核上运行,更新的值不会写入RAM以供其他内核查看;和

  2. int不是4字节对齐。

解决使用volatile关键字。在VS2005及更高版本中,C ++编译器使用内存障碍来访问这个变量,确保变量在使用之前总是完全写入/读取到主系统内存中。

I believe number 1 can be solved using the "volatile" keyword. In VS2005 and later the C++ compiler surrounds access to this variable using memory barriers, ensuring that the variable is always completely written/read to the main system memory before using it.

数字2我不能验证,我不知道为什么字节对齐会有所不同。我不知道x86指令集,但是 mov 需要给一个4字节对齐的地址吗?如果没有,你需要使用指令的组合吗?

Number 2 I cannot verify, I don't know why the byte alignment would make a difference. I don't know the x86 instruction set, but does mov need to be given a 4-byte aligned address? If not do you need to use a combination of instructions? That would introduce the problem.

因此...

问题1:使用volatile关键字(使用内存屏障的暗示和暗示编译器不优化此代码)使得程序员无需在读/写操作之间同步x86 / x64变量上的4字节/ 8字节?

QUESTION 1: Does using the "volatile" keyword (implicity using memory barriers and hinting to the compiler not to optimise this code) absolve a programmer from the need to synchronise a 4-byte/8-byte on x86/x64 variable between read/write operations?

问题2:是否明确要求变量为4字节/ 8字节对齐?

QUESTION 2: Is there the explicit requirement that the variable be 4-byte/8-byte aligned?

我对我们的代码和类中定义的变量进行了更多的挖掘:

I did some more digging into our code and the variables defined in the class:

class CExample
{

private:

    CRITICAL_SECTION m_Crit1; // Protects variable a
    CRITICAL_SECTION m_Crit2; // Protects variable b
    CRITICAL_SECTION m_Crit3; // Protects variable c
    CRITICAL_SECTION m_Crit4; // Protects variable d

    // ...

};

现在,对我来说这似乎过多了。我认为关键部分同步一个进程之间的线程,所以如果你有一个你可以输入它,没有其他线程在该进程可以执行。没有必要为你想要保护的每个变量设置一个关键部分,如果你在一个关键部分,那么没有什么可以打断你。

Now, to me this seems excessive. I thought critical sections synchronised threads between a process, so if you've got one you can enter it and no other thread in that process can execute. There is no need for a critical section for each variable you want to protect, if you're in a critical section then nothing else can interrupt you.

我认为可以从临界区以外更改变量的事情是,如果进程与另一个进程共享内存页(可以这样做?),另一个进程开始更改值。

I think the only thing that can change the variables from outside a critical section is if the process shares a memory page with another process (can you do that?) and the other process starts to change the values. Mutexes would also help here, named mutexes are shared across processes, or only processes of the same name?

问题3:我的分析是什么?关键部分是否正确,并且应该将此代码重写为使用互斥体?

QUESTION 3: Is my analysis of critical sections correct, and should this code be rewritten to use mutexes? I have had a look at other synchronisation objects (semaphores and spinlocks), are they better suited here?

问题4:关键部分的位置在哪里?我们已经看过其他同步对象(信号量和自旋锁) / mutexes / semaphores / spinlocks最适合?也就是说,应该应用哪个同步问题。对于选择其中之一,是否有巨大的性能损失?

QUESTION 4: Where are critical sections/mutexes/semaphores/spinlocks best suited? That is, which synchronisation problem should they be applied to. Is there a vast performance penalty for choosing one over the other?

虽然我们在上面,我看到自旋锁不应该用于单核多线程环境,只有一个多核多线程环境。

And while we're on it, I read that spinlocks should not be used in a single-core multithreaded environment, only a multi-core multithreaded environment. So, QUESTION 5: Is this wrong, or if not, why is it right?

感谢您提供任何回应:)

Thanks in advance for any responses :)

推荐答案

Q1:使用volatile关键字



Q1: Using the "volatile" keyword


在VS2005及更高版本中,C ++编译器使用内存屏障来访问这个变量,确保变量在使用之前总是完全写入/读取到主系统内存。

In VS2005 and later the C++ compiler surrounds access to this variable using memory barriers, ensuring that the variable is always completely written/read to the main system memory before using it.

正确。如果你没有创建可移植代码,Visual Studio就是这样实现的。如果你想要便携,你的选择目前有限。直到C ++ 0x没有可移植的方式如何指定原子操作与保证的读/写顺序,你需要实现每平台的解决方案。也就是说,提升已经为你做了肮脏的工作,你可以使用其原子基元

Exactly. If you are not creating portable code, Visual Studio implements it exactly this way. If you want to be portable, your options are currently "limited". Until C++0x there is no portable way how to specify atomic operations with guaranteed read/write ordering and you need to implement per-platform solutions. That said, boost already did the dirty job for you, and you can use its atomic primitives.

如果你保持对齐,你是安全的。如果没有,规则是复杂的(缓存行,...),因此最安全的方法是保持它们对齐,因为这很容易实现。

If you do keep them aligned, you are safe. If you do not, rules are complicated (cache lines, ...), therefore the safest way is to keep them aligned, as this is easy to achieve.

关键部分是一个轻量级互斥体。

Critical section is a lightweight mutex. Unless you need to synchronize between processes, use critical sections.

关键部分甚至可以为您自动等待

旋转锁使用以下事实:while等待CPU正在旋转,另一个CPU可能释放锁定。这不能发生在一个CPU,因此它只是一个浪费时间。在多CP​​U自旋锁可以是好主意,但它取决于多久旋转等待将成功。这个想法是等待一个短暂的时间是更快,然后做上下文切换那里,然后再次,因此如果等待它可能是短,最好等待。

Spin lock uses the fact that while the waiting CPU is spinning, another CPU may release the lock. This cannot happen with one CPU only, therefore it is only a waste of time there. On multi-CPU spin locks can be good idea, but it depends on how often the spin wait will be successful. The idea is waiting for a short while is a lot faster then doing context switch there and back again, therefore if the wait it likely to be short, it is better to wait.

这篇关于线程同步101的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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