![](/img/trans.png)
[英]Which types of memory_order should be used for non-blocking behaviour with an atomic_flag?
[英]Non-blocking pthread stop - or why does std::atomic_flag slow down my code
我实现了一个小的多线程应用程序,它执行以下操作:
主线程
一个主线程使用
setitimer
启动一个计时器,最多启动 8 个线程。 来自主线程的计时器用于从 > 文件中重复读取(每 0.25 秒)。 当计时器被调用 20 次(大约 5 秒后)时,我想停止线程并获取每个线程完成的计算量。
主线程.h
class MainThread {
private:
int counter;
ThreadManager tm;
bool registerTimer(double seconds);
void startTimerWithInterval(double interval);
void read() {
/**
* If counter >= 20, call stopWorker on all threads
*/
tm.stopWorkers();
}
public:
MainThread():counter(0){}
}
工作线程
在无限循环中执行一些昂贵的计算。 经过一定数量的计算后,线程必须存储它执行的计算次数。 这个值(计算量)必须非常准确,所以我想我必须立即停止线程(相当)。
线程类.h
class WorkerThread {
private:
/**
* ...
*/
std::atomic_flag keep_Running = ATOMIC_FLAG_INIT;
static void* run(void* args) {
((WorkerThread*)args)->process();
pthread_exit(nullptr);
return nullptr;
}
public:
/**
* ...
*/
bool startWorker() {
keep_Running.test_and_set();
bool result = (pthread_create(&thread, pthread_attr, run, this) == 0);
if(!result) {
keep_Running.clear();
}
return result;
}
void stopWorker() {
keep_Running.clear();
}
bool keepRunning() {
return keep_Running.test_and_set();
}
virtual void process() = 0;
};
计算线程.h
class ComputationThread : public WorkerThread {
public:
virtual void process() override {
/**
* Perform computations with ~400MB data
* check every 16B, whether keepRunning still true
*/
bool keep_running = true;
while(keep_running) {
/**
* Process 4B
*/
keep_running = keepRunning();
}
}
};
如果我使用某种标志来跟踪线程的运行状态,我必须使这个标志成为线程安全的,不是吗? 我尝试了std::atomic_flag
因为它应该是无锁的并且具有原子操作,但这会导致性能急剧下降。 我的问题是, std::atomic_flag
会导致性能下降,还是仅仅因为我执行检查方式过于频繁? 有谁知道更好的方法?
在你问之前,我必须使用pthread
而不是std::thread
将线程分配给线程创建中的指定核心(使用pthread_attrib_t
)。
不要使用std::atomic_flag
。
它是一种低级atomic
原语,因此具有非常有限的接口。
它的主要限制是您只能通过在名为test_and_set()
的单个atomic
调用test_and_set()
其设置为 true 来测试其值
这是一个读-修改-写操作 (RMW),它在所有内核之间执行昂贵的同步。 由于您在每次循环迭代时都调用它,因此它会显着减慢速度。
使用常规atomic<bool>
并在完成后设置它。 这样,在循环内部,您只需要读取它,这是一个atomic
负载,可以转换为常规的mov
操作。 设置特定的内存顺序不会影响性能(至少在 X86 上)。
std::atomic_flag::test_and_set()
包含std::memory_order
order = memory_order_seq_cst
的默认参数
标记
memory_order_seq_cst
原子操作不仅以与释放/获取排序相同的方式对内存进行排序(在一个线程中的存储之前发生的所有事情在执行加载的线程中成为可见的副作用),而且还建立了一个单一的总修改顺序所有被标记的原子操作。...
总顺序排序需要所有多核系统上的完整内存栅栏 CPU 指令。 这可能会成为性能瓶颈,因为它会迫使受影响的内存访问传播到每个内核。
memory_order
这个标志将导致每个线程按顺序执行test_and_set
内存操作,顺序加载和保存到内存中,这会变慢,因为每个线程将花时间等待其他线程执行它们的内存操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.