以下最小代码示例在 x64 调试模式下编译并在 Visual Studio 2013 调试器中运行,生成
未处理的异常位于...:访问冲突写入位置 0xFEEEFEEE。
调试时,我发现访问冲突发生在“return 0;”处 陈述。
(当在没有调试器的情况下从控制台运行时,错误显示“0x 处的指令...引用了 0xddddddd 处的内存...无法写入内存。”)。
#include <atomic>
#include <string>
int main()
{
std::atomic<std::string> a1("127.0.0.1:41001");
std::string ep1_1 = a1.load();
std::string ep1_2 = a1.load();
return 0;
}
Run Code Online (Sandbox Code Playgroud) 我不确定以下语句有什么问题,它给了我编译错误。我们不能将“auto”与原子变量一起使用吗?
#include <iostream>
#include<future>
#include <atomic>
using namespace std;
int main()
{
atomic<int> value(10);
auto NewValue = value;
}
Run Code Online (Sandbox Code Playgroud)
但如果我用“int”替换“auto”,它就可以工作。为什么?
int main()
{
atomic<int> value(10);
int NewValue = value;
}
Run Code Online (Sandbox Code Playgroud)
“auto”编译错误
||=== Build: Debug in Hello (compiler: GNU GCC Compiler) ===|
F:\3d\C++CodeProject\Hello\main.cpp||In function 'int main()':|
F:\3d\C++CodeProject\Hello\main.cpp|11|error: use of deleted function
'std::atomic<int>::atomic(const std::atomic<int>&)'|
C:\Program Files
(x86)\CodeBlocks\MinGW\lib\gcc\mingw32\5.1.0\include\c++\atomic|612|note:
declared here|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 1 second(s)) ===|
Run Code Online (Sandbox Code Playgroud) 我编写了一些多线程但无锁的代码,这些代码在较早的支持 C++11 的 GCC(7 或更早版本)上编译并显然执行得很好。原子场是ints 等等。据我所知,我使用普通的 C/C++ 操作a=1;在原子性或事件排序不是问题的地方对它们(等)进行操作。
后来我不得不做一些双宽 CAS 操作,并像常见的那样制作了一个带有指针和计数器的小结构。我尝试执行相同的普通 C/C++ 操作,但出现了变量没有此类成员的错误。(这是您对大多数普通模板的期望,但我半期望atomic以不同的方式工作,部分原因是支持正常的往返分配,据我所知,对于ints。)。
所以两部分问题:
我们是否应该在所有情况下都使用原子方法,甚至(比如)由一个没有竞争条件的线程完成的初始化?1a) 所以一旦声明为 atomic 就无法非原子地访问?1b)我们还必须使用atomic<>方法的冗长冗长来做到这一点?
否则,如果至少对于整数类型,我们可以使用普通的 C/C++ 操作。但在这种情况下,这些操作是否与load()/相同,store()或者它们只是正常的分配?
还有一个半元问题:是否有任何关于为什么atomic<>变量不支持普通 C/C++ 操作的见解?我不确定规范中的 C++11 语言是否有能力编写那样的代码,但规范肯定会要求编译器做一些事情,规范中的语言不够强大.
我想知道我们是否只是进行加载和存储之间是否有任何std::atomic<int>不同int。我不关心内存顺序。例如考虑下面的代码
int x{1};
void f(int myid) {
while(1){
while(x!= myid){}
//cout<<"thread : "<< myid<<"\n";
//this_thread::sleep_for(std::chrono::duration(3s));
x = (x % 3) + 1;
}
}
int main(){
thread x[3];
for(int i=0;i<3;i++){
x[i] = thread(f,i+1);
}
for(int i=0;i<3;i++){
x[i].join();
}
}
Run Code Online (Sandbox Code Playgroud)
现在输出(如果取消注释 cout)将是
线程:1
螺纹数:2
螺纹数:3
...
int x我想知道更改为有什么好处吗atomic<int> x?
让我们考虑下一个片段:
int val=5;
int& ref=val;
std::atomic<int> atomicref(ref);
++atomicref;
std::cout<< "atomic ref="<<atomicref.load()<<" original ref="<<ref<<" original val="<<val;
Run Code Online (Sandbox Code Playgroud)
当我在 Mac OS X、XCode 8.3.3、c++11 下编译它时,我收到如下输出:
atomic ref=6 original ref=5 original val=5
Run Code Online (Sandbox Code Playgroud)
该行:
std::atomic<int> atomicref(ref);
当然看起来很可疑,因为原子下的类型与变量声明中的类型不同 - 它是引用。
我想知道为什么这些值不匹配;说atomicref实际上创建了val的副本是否正确?
我有一个类,用于数据存储,其中只有一个实例。
调用者是消息驱动的,并且变得太大,是重构的主要候选者,这样每个消息都由单独的线程处理。然而,这些可能会竞争读/写数据。
如果我使用互斥体(mutices?),我只会在写操作上使用它们。我认为这并不重要,因为数据是原子的,而不是访问数据的函数。
有没有什么简单的方法可以使所有数据原子化?目前它由简单类型、向量和其他类的对象组成。如果我必须添加std::atomic<>到每个子字段,我也可以使用互斥体。
我谈到的例子是cppreference.com 上的这个例子。代码片段粘贴在下面。
int main(){
const std::size_t ThreadNumber = 5;
const int Sum = 5;
std::atomic<int> atom{0};
std::atomic<int> counter{0};
// lambda as thread proc
auto lambda = [&](const int id){
for (int next = 0; next < Sum;){
// each thread is writing a value from its own knowledge
const int current = atom.exchange(next);
counter++;
// sync writing to prevent from interrupting by other threads
std::osyncstream(std::cout)
<< '#' << id << " (" << std::this_thread::get_id()
<< ") wrote " …Run Code Online (Sandbox Code Playgroud) 我在几个地方读到,宽松的排序可以生成唯一的 ID。我对此表示怀疑,因为如果两个线程同时调用:
uniqueId.fetch_add(1, std::memory_order::relaxed);
那么线程 A 递增的值可能对线程 B 不可见。这意味着,两个线程可以获得相同的唯一 ID。
出于这个原因,我宁愿使用std::memory_order::acq_rel
你怎么认为?
在实践中无法测试。
使用现代 C++,拥有由一个线程初始化的共享内存(第一个到达这一点,然后由多个线程读取)的最佳方法是什么?它需要尽可能轻。
int *ptr = nullptr;
void parallel_work() {
// this should only done by the first thread to this point
ptr = init_ptr();
// all threads read After ptr is set
do_with(ptr);
}
int main() {
std::thread th0 { ¶llel_work };
std::thread th1 { ¶llel_work };
std::thread th2 { ¶llel_work };
parallel_work();
}
Run Code Online (Sandbox Code Playgroud)
如果它可以帮助的话,我真的想避免将代码的整个读取部分包装在mutex.
PS:这不是static函数变量的用例,因为我将在程序的生命周期中创建其中的许多变量。
阅读文本,因为std::condition_variable我遇到了这句话:
即使共享变量是原子的,也必须在互斥锁下进行修改,才能将修改正确发布到等待线程。
我的问题是这样的:
如果不是“与 POD 一起工作的无锁代码”,原子有什么用?
更新
看起来我的问题有些混乱:(
引用文本中的“共享变量”与“条件变量”不同。请参阅同一页面中的此引用:
...直到另一个线程同时修改共享变量(条件),并通知condition_variable
请不要回答“为什么我们需要使用带有条件变量的互斥锁”或“条件等待如何工作”,而是提供有关互斥锁的使用如何“正确发布”对等待线程的原子修改的信息,即是否需要在互斥锁下完成像++counter;(而不是像测试if(counter == 0))这样的表达式?
我知道GCC 链接库-latomic需要该标志。atomic但是,由于某种原因std::atomic<int>不需要它来构建
而结构体则
这种差异是由什么造成的呢?