是否需要使用互斥锁?

发布于 2024-12-08 12:25:32 字数 416 浏览 0 评论 0原文

简单版本:在 C++ 程序中,我使用两个不同的线程来处理某些整数变量。但我确信一个人总是在其中写入一些值,而另一个人只是在读取它。读/写数据时还需要使用互斥锁吗?

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

如果进行任何更改,我正在为基于 Linux 的系统开发,并且代码是使用 gcc 编译的。

PS:在一些使用线程的代码中,我看到不同线程之间共享的变量周围有关键字易失性,我也需要使用它吗?

Simple edition: In a C++ program I'm using two different threads to work with some integer variable. but I'm sure one is always writing some value into it and the other one is only reading That. do I still need to use mutex lock when reading/writing data?

Now details : The main idea is that first thread generates some information and saves them into an array, and the second thread reads data from that array and process them. this array represents a queue. meaning I have two index values pointing to the first and last item in queue. Now I'm wondering if I have to lock these two index values whenever I'm reading or writing values or is it ok to check them without locking? note that generator thread is the only thread changing index of queue_back, and processor thread has exclusive permission to change queue_front.

if makes any change I'm developing for a linux based system and the code is compiled using gcc.

PS: In some codes which use threading, I've seen keyword volatile around variables shared between different threads, do I need to use that too?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

套路撩心 2024-12-15 12:25:32

不,读取和写入不是原子的,您需要使用某种同步机制来同步它。

另外,您必须将共享整数标记为易失性,否则优化器可能会认为该变量永远不会在您的线程之一中更新。

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);

No the read and write are not atomic, You will need to synchronize it using some synchronization mechanism.

Also, You MUST mark the shared integer as volatile, otherwise the optimizer might think the variable is never updated in one of your threads.

gcc allows you to do atomic operations on int‘s, long‘s and long long‘s (and their unsigned counterparts).

Look up for the functions:

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);
娇纵 2024-12-15 12:25:32

读/写数据时还需要使用互斥锁吗?

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

您还可以使用原子和/或内存屏障。使用这些需要更好地了解您的目标架构。重现多线程错误可能非常困难,并且这些替代方案应被视为可能不可移植的优化。

我已经看到不同线程之间共享的变量周围有关键字 volatile,我也需要使用它吗?

哎呀。不!这不是 C++ 中多线程读写的安全或可移植解决方案。使用原子、锁、复制、不可变和纯实现(等等)来代替。

易失性的解释可能因平台和/或编译器而异,并且没有指定它在 C 或 C++ 中以任何特定方式进行操作以实现多线程读取和写入(有一个古老的错误传说,它可以是可靠地用作原子读/写)。我曾经在一个多线程 C++ 程序中测试了 volatile 的有效性(在带有苹果 gcc 的 intel-mac 上)。我不会提供结果,因为它的效果足够好以至于有些人可能会考虑使用它,尽管他们不应该因为“几乎”还不够好。

为了限定 volatile 的使用:它存在于我的(大型、严格编写的、多线程感知的)代码库中,其唯一目的是与平台相关的原子 API 进行交互。老实说:早期还有一些其他用途,但它们可以而且应该被删除。

Do I still need to use mutex lock when reading/writing data?

Yes, you will need a lock. You may be interested in a more specific implementation called a read/write lock.

You can also use atomics and/or memory barriers. Using these will require a better understanding of your target architectures. Reproducing multithreading bugs can be very difficult, and these alternatives should be considered an optimization which may not be portable.

I've seen keyword volatile around variables shared between different threads, do I need to use that too?

Yikes. No! That is not a safe or portable solution to multithreaded reads and writes in C++. Use atomics, locks, copying, immutable and pure implementations (etc...) instead.

Interpretation of volatile can vary by platform and/or compiler, and it's not specified to operate any particular way in C or C++ for the purpose of multithreaded reads and writes (there's an old false legend that it can be used reliably as an atomic read/write). I once tested the effectiveness of volatile in a multithreaded C++ program for fun (on an intel-mac with apple's gcc). I won't provide the results because it worked well enough that some people might consider using it, although they should not because 'almost' isn't good enough.

And to qualify the use of volatile: It exists in my (large, strictly written, multithreading aware) codebase for the sole purpose of interfacing with platform dependent atomics APIs. And being entirely honest: there are a few other uses from earlier days, but they can and should be removed.

心舞飞扬 2024-12-15 12:25:32

是的,您需要使用互斥体、临界区、互锁访问等来同步对变量的访问,以确保读取线程在写入线程仍在保存字节时不会读取不完整的字节。这对于多核/CPU 系统尤其重要,其中两个线程可以真正并行地访问变量。

Yes, you need to synchronize access to the variable, either with a mutex, a critical section, interlocked access, etc to make sure that the reading thread does not read incomplete bytes while the writing thread is still saving them. This is especially important on multi-core/CPU systems, where the two threads can truely access the variable in parallel.

幽梦紫曦~ 2024-12-15 12:25:32

在大多数主要架构上,读取和写入不大于机器字(通常是 int 解析为的任何内容)的正确对齐数据是原子的。这并不意味着所有架构。

这意味着不,您不能只读取 headtail 并期望数据是一致的。但是,如果 sizeof(int) 恰好是 4 并且 sizeof(short) 恰好是 2,并且如果您不关心“非主流”平台,您可以进行一些联合欺骗,并且无需原子操作或互斥锁即可逃脱。

如果您希望代码可移植,则无法绕过正确的锁定或原子比较/交换。

关于易失性,这确实为Microsoft Visual C++插入了内存屏障(作为特定于编译器的诡辩),但标准保证任何特殊的东西除此之外,编译器不会优化该变量。到目前为止,使一些东西易失性并没有多大帮助,而且它根本无法保证线程安全。

Reads and writes of properly aligned data no larger than a machine word (usually whatever int resolves to) are atomic on most major architectures. That does not mean every architecture.

This means that no, you cannot just read head and tail and expect that data is consistent. However, if for example sizeof(int) happens to be 4 and sizeof(short) happens to be 2, and if you don't care about "not mainstream" platforms, you can do some union trickery and get away without atomic operations or a mutex.

If you want your code to be portable, there is no way around proper locking or atomic compare/exchange.

About volatile, this does insert a memory barrier for Microsoft Visual C++ (as a compiler-specific sophistry) but the standard does not guarantee anything special other than that the compiler won't optimize the variable. Insofar, making something volatile does not really help much, and it guarantees thread safety in no way.

女中豪杰 2024-12-15 12:25:32

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

华泰

Yes, you need to protect the queue indexes in both the generator and the reader threads by using some kind of synchronization mechanism, like a mutex.

HTH

葬花如无物 2024-12-15 12:25:32

如果您确实只是共享一个整数,那么来自 标头的 std::atomic 听起来像是正确使用的类型。 (如果您有旧的编译器,还应该有 Boost 或 TR1 版本。)这确保了原子读取和写入。据我所知,不需要 易失性 限定符。

If you're really just sharing one single integer, then std::atomic<int> sounds like the right type to use, from the <atomic> header. (There should also be Boost or TR1 versions if you have an old compiler.) This ensures atomic reads and writes. There's no need for a volatile qualifier as far as I understand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文