2011-10-05 153 views
1

简单版:在一个C++程序中,我使用两个不同的线程来处理一些整数变量。但我相信一个人总是在写一些价值,另一个人只是在阅读那篇文章。在读取/写入数据时是否还需要使用互斥锁?确实需要使用互斥锁吗?

现在的细节:主要思想是第一个线程生成一些信息并将它们保存到一个数组中,第二个线程从该数组中读取数据并处理它们。这个数组表示一个队列。这意味着我有两个索引值指向队列中的第一个和最后一个项目。现在我想知道,如果我在读取或写入数值时必须锁定这两个索引值,还是可以不锁定地检查它们?请注意,生成器线程是queue_back的唯一线程更改索引,并且处理器线程具有更改queue_front的独占权限。

如果我正在为基于Linux的系统开发任何更改,并使用gcc编译代码。 PS:在一些使用线程的代码中,我看到关键字volatile围绕着不同线程之间共享的变量,我是否也需要使用它呢?

+0

如果未使用volatile,其他线程可能会或可能不会看到对该变量所做的更改。 –

回答

3

否读取和写入不是原子的,您需要使用某种同步机制对其进行同步。另外,您必须将共享整数标记为volatile,否则优化器可能会认为该变量永远不会在您的一个线程中更新。

gcc允许您对int,long和long long(以及它们的未签名对象)执行原子操作。

查找的功能:

type __sync_fetch_and_add (type *ptr, type value); 
type __sync_fetch_and_sub (type *ptr, type value); 
type __sync_fetch_and_or (type *ptr, type value); 
type __sync_fetch_and_and (type *ptr, type value); 
type __sync_fetch_and_xor (type *ptr, type value); 
type __sync_fetch_and_nand (type *ptr, type value); 
+0

正要输入类似的东西。 –

+1

建议编辑“会认为”“可能会想”。 –

+0

@JohnDibling:同意和修改。 –

1

是的,你需要一个互斥体,关键段,联锁接入等同步访问变量,既可以确保读取线程不写入线程仍然保存它们时读取不完整的字节。这对于多核/ CPU系统来说尤为重要,在这些系统中,两个线程可以真正并行访问变量。

+0

如果将数组的大小限制为256,并将索引值限制为单个字节的数据,该怎么办? – Ali1S232

0

是的,您需要通过使用某种同步机制(如互斥锁)来保护生成器和读取器线程中的队列索引。

HTH

+1

等一下,真的吗?编译器不允许优化它呢?我不认为这是正确的。这是'volatile'关键字的唯一用法。 –

+0

@Minging Duck - 是的,你是对的,我错了。我改变了答案。感谢您接受它。 –

0

如果你真的只是共享一个单一的整数,然后std::atomic<int>听起来像是正确的类型使用,从<atomic>头。 (如果你有一个旧的编译器,还应该有Boost或TR1版本。)这确保了原子读取和写入。据我所知,不需要volatile限定词。

+0

好吧,它不仅仅是一个整数,我分享,但只有一个整数可以在同一时间访问。我的意思是整个队列变量是共享的,但是两个线程在尝试访问队列节点之前首先检查头部和尾部变量。 – Ali1S232

1

读取和写入正确对齐的数据不超过机器字(通常任何int可以解决的问题)在大多数主要体系结构上都是原子的。那不是是指架构。

这意味着不,您不能只读headtail并期望数据是一致的。但是,例如,如果sizeof(int)碰巧是4,而sizeof(short)碰巧是2,并且如果您不关心“非主流”平台,则可以进行一些联合欺骗并在没有原子操作或互斥体的情况下离开。

如果您希望自己的代码具有可移植性,则无法实现正确的锁定或原子比较/交换。

关于volatile,这插入用于Microsoft Visual C++(作为一个特定的编译器狡辩)记忆障碍,但标准不保证什么特别的东西以外,编译器不会优化变量。就目前而言,制作volatile并没有什么帮助,它确保了线程的安全性。

1

在读取/写入数据时,我仍然需要使用互斥锁吗?

是的,你需要一个锁。您可能对称为读/写锁的更具体实现感兴趣。

您还可以使用原子和/或内存屏障。使用这些将需要更好地理解您的目标体系结构。重现多线程错误可能非常困难,而这些替代方案应该被认为是可能不便携的优化。

我看过关键字在不同线程之间共享变量,我是否也需要使用它呢?

哎呀。没有!这不是C++中多线程读取和写入的安全或可移植解决方案。代之以使用原子,锁定,复制,不可变和纯粹的实现(等等)。

volatile解读可以通过平台和/或编译器而变化,并且它没有指定的操作用C任何特定方式或C++多线程的目的读取和写入(有,它可以被可靠地用作旧假图例原子读/写)。我曾经在一个多线程的C++程序中测试了volatile的有效性(在苹果的gcc的intel-mac上)。我不会提供结果,因为它的工作原理是有些人可能会考虑使用它,尽管他们应该不是因为'几乎'不够好。

并限定使用volatile:它存在于我的(大型,严格编写,多线程感知)代码库中,仅用于与平台相关的原子API进行接口。而且完全诚实:早些时候还有其他一些用途,但它们可以并且应该被删除。