简体   繁体   English

gcc原子读写

[英]gcc atomic read and writes

I have a multithreaded application where I one producer thread(main) and multiple consumers. 我有一个多线程应用程序,其中我有一个生产者线程(主)和多个消费者。

Now from main I want to have some sort of percentage of how far into the work the consumers are. 现在,从主要方面来说,我希望在消费者的工作中有多少比例。 Implementing a counter is easy as the work that is done a loop. 实现计数器很容易,因为循环完成的工作。 However since this loop repeats a couple of thousands of times, maybe even more than a million times. 然而,由于这个循环重复了几千次,甚至可能超过一百万次。 I don`t want to mutex this part. 我不想互斥这个部分。 So I went looking into some atomic options of writing to an int. 所以我开始研究写入int的一些原子选项。

As far as I understand I can use the builtin atomic functions from gcc: https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html 据我所知,我可以使用gcc中的内置原子函数: https//gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

however, it doesn`t have a function for just reading the variable I want to work on. 但是,它没有只读取我想要处理的变量的功能。

So basically my question is. 基本上我的问题是。

  1. can I read from the variable safely from my producer, as long as I use the atomic builtins for writing to that same variable in the consumer 我可以安全地从我的生产者那里读取变量,只要我使用原子内置函数来写入消费者中的同一个变量

or 要么

  1. do I need some sort of different function to read from the variable. 我需要某种不同的函数来从变量中读取。 and what function is that 那是什么功能

Define "safely". 定义“安全”。

If you just use a regular read, on x86, for naturally aligned 32-bit or smaller data, the read is atomic, so you will always read a valid value rather than one containing some bytes written by one thread and some by another. 如果您只是在x86上使用常规读取,对于自然对齐的32位或更小的数据,读取是原子的,因此您将始终读取有效值,而不是包含由一个线程和一些线程写入的某些字节的值。 If any of those things are not true (not x86, not naturally aligned, larger than 32 bits...) all bets are off. 如果这些事情中的任何一个都不是真的(不是x86,不是自然对齐,大于32位......)所有的赌注都是关闭的。

That said, you have no guarantee whatsoever that the value read will be particularly fresh, or that the sequence of values seen over multiple reads will be in any particular order. 也就是说,您无法保证所读取的值特别新鲜,或者在多次读取中看到的值序列将以任何特定顺序排列。 I have seen naive code using volatile to defeat the compiler optimising away the read entirely but no other synchronisation mechanism, literally never see an updated value due to CPU caching. 我已经看到使用volatile天真代码打败了编译器完全优化了读取而没有其他同步机制,从而没有看到由于CPU缓存而更新的值。

If any of these things matter to you, and they really should, you should explicitly make the read atomic and use the appropriate memory barriers. 如果这些事情中的任何一个对您很重要,而且它们确实应该,那么您应该明确地使读取原子并使用适当的内存屏障。 The intrinsics you refer to take care of both of these things for you: you could call one of the atomic intrinsics in such a way that there is no side effect other than returning the value: 你引用的内在函数为你处理这两件事:你可以调用一个原子内在函数,除了返回值之外没有任何副作用:

__sync_val_compare_and_swap(ptr, 0, 0)

or 要么

__sync_add_and_fetch(ptr, 0)

or 要么

__sync_sub_and_fetch(ptr, 0)

or whatever 管他呢

If your compiler supports it, you can use C11 atomic types. 如果您的编译器支持它,您可以使用C11原子类型。 They are introduced in the section 7.17 of the standard, but they are unfortunately optional, so you will have to check whether __STDC_NO_ATOMICS__ is defined to at least throw a meaningful error if it's not supported. 它们在标准的7.17节中介绍,但遗憾的是它们是可选的,因此如果不支持,则必须检查是否定义了__STDC_NO_ATOMICS__以至少抛出有意义的错误。

With gcc , you apparently need at least version 4.9, because otherwise the header is missing ( here is a SO question about this, but I can't verify because I don't have GCC-4.9 ). 使用gcc ,你显然至少需要版本4.9,因为否则标题丢失( 这是一个关于这个的SO问题,但我无法验证,因为我没有GCC-4.9 )。

I'll answer your question, but you should know upfront that atomics aren't cheap. 我会回答你的问题,但你应该事先知道原子并不便宜。 The CPU has to synchronize between cores every time you use atomics, and you won't like the performance results if you use atomics in a tight loop. 每次使用原子时,CPU都必须在内核之间进行同步,如果在紧密循环中使用原子,则不会喜欢性能结果。

The page you linked to lists atomic operations for the writer , but says nothing about how such variables should be read. 您链接到列表中的原子操作的作家 ,但该页面没有提到这样的变量应该如何读。 The answer is that your other CPU cores will "see" the updated values, but your compiler may "cache" the old value in a register or on the stack. 答案是您的其他CPU内核将“看到”更新的值,但您的编译器可能会将旧值“缓存”在寄存器或堆栈中。 To prevent this behavior, I suggest you declare the variable volatile to force your compiler not to cache the old value. 为防止出现这种情况,我建议您声明变量volatile以强制编译器不要缓存旧值。

The only safety issue you will encounter is stale data, as described above. 如上所述,您将遇到的唯一安全问题是陈旧数据。

If you try to do anything more complex with atomics, you may run into subtle and random issues with the order atomics are written to by one thread versus the order you see those changes in another thread. 如果你试图用原子做更复杂的事情,你可能遇到一个细微而随机的问题,一个线程写的命令原子与你在另一个线程中看到这些改变的顺序。 Unfortunately you're not using a built-in language feature, and the compiler builtins aren't designed perfectly. 遗憾的是,您没有使用内置语言功能,并且编译器内置设计并不完美。 If you choose to use these builtins, I suggest you keep your logic very simple. 如果您选择使用这些内置函数,我建议您保持逻辑非常简单。

If I understood the problem, I would not use any atomic variable for the counters. 如果我理解了这个问题,我就不会对计数器使用任何原子变量。 Each worker thread can have a separate counter that it updates locally, the master thread can read the whole array of counters for an approximate snapshot value, so this becomes a 1 consumer 1 producer problem. 每个工作线程可以有一个单独的计数器,它在本地更新,主线程可以读取整个计数器阵列的近似快照值,因此这成为1个消费者1生产者问题。 The memory can be made visible to the master thread, for example, every 5 seconds, by using __sync_synchronize() or similar. 通过使用__sync_synchronize()或类似内容,可以使主线程对内存可见,例如,每5秒一次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM