我实现了一个小的多线程应用程序,它执行以下操作:
MainThread
主线程使用
setitimer
启动计时器并启动最多8个 线程。来自主线程的计时器用于从>重复读取。一个文件(每0.25秒)。当计时器被调用20次(约5秒后)时,我 想要停止线程并完成计算量 每个帖子。
MainThread.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){}
}
WorkerThreads
在无限循环中执行一些昂贵的计算。 经过一定量的计算后,线程必须存储 它执行的计算次数。这个值(计算量) 必须非常准确,所以我认为我必须停止线程(相当) 立刻。
ThreadClass.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;
};
ComputationThread.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
)。
答案 0 :(得分:1)
不要使用std::atomic_flag
。
它意味着低级atomic
原语,因此界面非常有限
它的主要限制是,您只能通过在名为atomic
的单test_and_set()
次呼叫中将其设置为true来测试其值。
这是一种读 - 修改 - 写操作(RMW),可在所有内核之间执行昂贵的同步。
由于您在每次循环迭代时都调用它,因此它会显着减慢。
使用常规atomic<bool>
并在完成后进行设置。
这样,在循环内部,您只需要读取它,这是一个atomic
加载,并转换为常规mov
操作。
设置特定的内存顺序对性能没有影响(至少在X86上)。
答案 1 :(得分:0)
std::atomic_flag::test_and_set()
包含默认参数std::memory_order
order = memory_order_seq_cst
标记
memory_order_seq_cst
的原子操作不仅以与释放/获取排序相同的方式命令内存(发生的所有事情 - 在一个线程中的存储变为在执行加载的线程中的可见副作用之前),而且还建立所有标记的原子操作的单个总修改顺序。...
总顺序排序需要在所有多核系统上使用完整的内存屏障CPU指令。 这可能会成为性能瓶颈,因为它会强制受影响的内存访问传播到每个核心。
memory_order
的这个标志将导致每个线程按顺序执行test_and_set
的内存操作,顺序加载并保存到内存,随着每个线程的运行速度会变慢花时间等待其他线程执行内存操作。