读取互锁变量 [英] Reading interlocked variables

查看:167
本文介绍了读取互锁变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设:



A。 C ++在WIN32下。



B。正确对齐的挥发性整数使用 InterlockedIncrement() InterlockedDecrement()增加和减少。

  __ declspec(align(8))volatile LONG _ServerState = 0; 

如果我只想读_ServerState,我需要通过 InterlockedXXX function?



例如,我看到了如下代码:

  LONG x = InterlockedExchange(& _ServerState,_ServerState); 

  LONG x = InterlockedCompareExchange(& _ServerState,_ServerState,_ServerState); 

目标是简单地读取 _ServerState



我不能简单地说:

  if(_ServerState == some value)
{
// blah blah blah
}


b $ b

似乎有一些混乱WRT这个主题。我理解寄存器大小的读取在Windows中是原子的,所以我认为 InterlockedXXX 函数是不必要的。



Matt J。






好,感谢您的回应。 BTW,这是Visual C ++ 2005和2008.



如果是真的,我应该使用 InterlockedXXX 函数读取 _ServerState 的值,即使只是为了清楚起见,最好的方法是什么?

  LONG x = InterlockedExchange(& _ServerState,_ServerState); 

当我真正想做的是读取它时,这有修改值的副作用。不仅如此,但如果有一个上下文切换,因为在准备中将 _ServerState 的值推入堆栈,我有可能将标志重置为错误的值调用 InterlockedExchange()

  LONG x = InterlockedCompareExchange(& _ServerState,_ServerState,_ServerState); 

我从MSDN上看到的示例中获取了这个。

参见< a href =http://msdn.microsoft.com/en-us/library/ms686355(VS.85).aspx> http://msdn.microsoft.com/en-us/library/ms686355(VS。 85).aspx



所有我需要的是沿着这些线:

  lock mov eax,[_ServerState] 

无论如何,我想是明确的,是提供线程安全访问一个标志,而不会招致关键部分的开销。我已经看到LONGs使用这种方式通过 InterlockedXXX()家庭的函数,因此我的问题。



我们正在考虑一个很好的解决这个问题的读取当前值是:

  LONG Cur = InterlockedCompareExchange(& _ServerState,0 ,0); 


解决方案

这取决于你的意思是只需读取_ServerState的当前值,它取决于使用什么套工具和平台(您指定Win32和C ++,但不是哪个C ++编译器,这可能很重要)。



如果你只是想读取值,使得值是未损坏的(即,如果一些其他处理器正在将值从0x12345678更改为0x87654321您的读取将获得这两个值之一,而不是0x12344321)然后简单只要变量为:




  • 标记为 volatile / li>
  • 正确对齐,

  • 使用单个指令读取处理器原子处理的字大小



这不是C / C ++标准所承诺的,但是Windows和MSVC确实做出了这些保证,我认为大多数编译器目标Win32也是这样。



但是,如果你想让你的读取与另一个线程的行为同步,还有一些额外的复杂性。假设您有一个简单的邮箱协议:

  struct mailbox_struct {
uint32_t flag;
uint32_t data;
};
typedef struct mailbox_struct volatile mailbox;


//全局 - 线程启动前初始化

邮箱mbox = {0,0};

// *************************
//线程A

while(mbox.flag == 0){
/ * spin ... * /
}

uint32_t data = mbox.data;

// ***************************

// *** ************************
//线程B

mbox.data = some_very_important_value;
mbox.flag = 1;

// ***************************

思考是线程A会自动等待mbox.flag指示mbox.data有一条有效的信息。线程B会将一些数据写入mailbox.data,然后将mbox.flag设置为1,作为mbox.data有效的信号。



在这种情况下,mbox.flag的线程A中的简单读取可能获得值1,即使后续读取线程A中的mbox.data未获取值这是因为即使编译器不会将线程B写入重新排序到mbox.data和mbox.flag,处理器和/或缓存也可能会在线程B中写入。



< 。 C / C ++保证编译器将生成代码,使线程B在写入mbox.flag之前写入mbox.data,但处理器和缓存可能有不同的想法 - 特殊处理称为内存屏障或获取和释放语义必须用于确保在线程的指令流的级别以下。



我不确定除MSVC之外的编译器是否有任何关于排序的声明指令级别。然而MS确保对于MSVC,volatile是足够的 - MS指定volatile写入具有释放语义和volatile读取具有获取语义 - 虽然我不确定在哪个版本的MSVC这适用 - 参见http://msdn.microsoft.com/en-us/library/12a04hfd.aspx?ppud=4 。 / p>

我也看到了像你描述的代码,它使用互锁的API来执行简单的读取和写入共享位置。我的问题是使用互锁的API。锁免费的线程间通信是非常难以理解和微妙的陷阱,并试图采取一个关键位的代码的快捷方式,可能最终以一个非常难以诊断的错误似乎不是一个好主意,我。此外,使用互锁API会向任何维护代码的人发出尖叫,这是需要与其他内容共享或同步的数据访问 - 仔细!



当使用联锁API时,你将硬件和编译器的细节带出图片 - 平台确保所有的东西都被正确处理 - p>

阅读 Herb Sutter的有效并发文章对DDJ(这是碰巧,在这一刻,至少为我)关于这个主题的好信息。


Assume:

A. C++ under WIN32.

B. A properly aligned volatile integer incremented and decremented using InterlockedIncrement() and InterlockedDecrement().

__declspec (align(8)) volatile LONG _ServerState = 0;

If I want to simply read _ServerState, do I need to read the variable via an InterlockedXXX function?

For instance, I have seen code such as:

LONG x = InterlockedExchange(&_ServerState, _ServerState);

and

LONG x = InterlockedCompareExchange(&_ServerState, _ServerState, _ServerState);

The goal is to simply read the current value of _ServerState.

Can't I simply say:

if (_ServerState == some value)
{
// blah blah blah
}

There seems to be some confusion WRT this subject. I understand register-sized reads are atomic in Windows, so I would assume the InterlockedXXX function is unnecessary.

Matt J.


Okay, thanks for the responses. BTW, this is Visual C++ 2005 and 2008.

If it's true I should use an InterlockedXXX function to read the value of _ServerState, even if just for the sake of clarity, what's the best way to go about that?

LONG x = InterlockedExchange(&_ServerState, _ServerState);

This has the side effect of modifying the value, when all I really want to do is read it. Not only that, but there is a possibility that I could reset the flag to the wrong value if there is a context switch as the value of _ServerState is pushed on the stack in preparation of calling InterlockedExchange().

LONG x = InterlockedCompareExchange(&_ServerState, _ServerState, _ServerState);

I took this from an example I saw on MSDN.
See http://msdn.microsoft.com/en-us/library/ms686355(VS.85).aspx

All I need is something along the lines:

lock mov eax, [_ServerState]

In any case, the point, which I thought was clear, is to provide thread-safe access to a flag without incurring the overhead of a critical section. I have seen LONGs used this way via the InterlockedXXX() family of functions, hence my question.

Okay, we are thinking a good solution to this problem of reading the current value is:

LONG Cur = InterlockedCompareExchange(&_ServerState, 0, 0);

解决方案

It depends on what you mean by "goal is to simply read the current value of _ServerState" and it depends on what set of tools and the platform you use (you specify Win32 and C++, but not which C++ compiler, and that may matter).

If you simply want to read the value such that the value is uncorrupted (ie., if some other processor is changing the value from 0x12345678 to 0x87654321 your read will get one of those 2 values and not 0x12344321) then simply reading will be OK as long as the variable is :

  • marked volatile,
  • properly aligned, and
  • read using a single instruction with a word size that the processor handles atomically

None of this is promised by the C/C++ standard, but Windows and MSVC do make these guarantees, and I think that most compilers that target Win32 do as well.

However, if you want your read to be synchronized with behavior of the other thread, there's some additional complexity. Say that you have a simple 'mailbox' protocol:

struct mailbox_struct {
    uint32_t flag;
    uint32_t data;
};
typedef struct mailbox_struct volatile mailbox;


// the global - initialized before wither thread starts

mailbox mbox = { 0, 0 };

//***************************
// Thread A

while (mbox.flag == 0) { 
    /* spin... */ 
}

uint32_t data = mbox.data;

//***************************

//***************************
// Thread B

mbox.data = some_very_important_value;
mbox.flag = 1;

//***************************

The thinking is Thread A will spin waiting for mbox.flag to indicate mbox.data has a valid piece of information. Thread B will write some data into mailbox.data then will set mbox.flag to 1 as a signal that mbox.data is valid.

In this case a simple read in Thread A of mbox.flag might get the value 1 even though a subsequent read of mbox.data in Thread A does not get the value written by Thread B.

This is because even though the compiler will not reorder the Thread B writes to mbox.data and mbox.flag, the processor and/or cache might. C/C++ guarantees that the compiler will generate code such that Thread B will write to mbox.data before it writes to mbox.flag, but the processor and cache might have a different idea - special handling called 'memory barriers' or 'acquire and release semantics' must be used to ensure ordering below the level of the thread's stream of instructions.

I'm not sure if compilers other than MSVC make any claims about ordering below the instruction level. However MS does guarantee that for MSVC volatile is enough - MS specifies that volatile writes have release semantics and volatile reads have acquire semantics - though I'm not sure at which version of MSVC this applies - see http://msdn.microsoft.com/en-us/library/12a04hfd.aspx?ppud=4.

I have also seen code like you describe that uses Interlocked APIs to perform simple reads and writes to shared locations. My take on the matter is to use the Interlocked APIs. Lock free inter-thread communication is full of very difficult to understand and subtle pitfalls, and trying to take a shortcut on a critical bit of code that may end up with a very difficult to diagnose bug doesn't seem like a good idea to me. Also, using an Interlocked API screams to anyone maintaining the code, "this is data access that needs to be shared or synchronized with something else - tread carefully!".

Also when using the Interlocked API you're taking the specifics of the hardware and the compiler out of the picture - the platform makes sure all of that stuff is dealt with properly - no more wondering...

Read Herb Sutter's Effective Concurrency articles on DDJ (which happen to be down at the moment, for me at least) for good information on this topic.

这篇关于读取互锁变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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