我有一个同时由2个线程使用的类:一个线程将结果(逐个)添加到任务的results
,第二个线程处理那些results
那个已经在那里了。
// all members are copy-able
struct task {
command cmd;
vector<result> results;
};
class generator {
public:
generator(executor* e); // store the ptr
void run();
...
};
class executor {
public:
void run();
void add_result(int command_id, result r);
task& find_task(int command_id);
...
private:
vector<task> tasks_;
condition_variable_any update_condition_;
};
启动
// In main, we have instances of generator and executor,
// we launch 2 threads and wait for them.
std::thread gen_th( std::bind( &generator::run, gen_instance_) );
std::thread exe_th( std::bind( &executor::run, exe_instance_) );
生成器线程
void generator::run() {
while(is_running) {
sleep_for_random_seconds();
executor_->add_result( SOME_ID, new_result() );
}
}
执行人员线程
void executor::add_result( int command_id, result r ) {
std::unique_lock<std::recursive_mutex> l(mutex_);
task& t = this->find_task(command_id);
t.results.push_back(r);
update_condition_.notify_all();
}
void executor::run() {
while(is_running) {
update_condition_.wait(...);
task& t = this->find_task(SOME_ID);
for(result r: t.results) {
// no live updates are visible here
}
}
}
executor
。它通过run
方法运行,该方法等待更新,当发生这种情况时,它会对结果起作用。很少有事情需要注意:
for-each
循环获取它正在处理的任务,然后迭代结果,检查哪些是新的并处理它们。处理完成后,它们会被标记,不会再次处理。此处理可能需要一些时间。当执行程序线程在添加另一个结果之前没有完成for循环时,会出现问题 - 结果对象在for循环中不可见。由于执行程序线程正在运行,它不会注意到更新条件更新,不刷新向量等。完成后(处理 alread-not-actual视图 tasks_
)它再次挂起update_condition_
..刚被触发。
我需要让代码知道,它应该在完成后再次运行循环或对for-each
循环中可见的任务进行更改。这个问题的最佳解决方案是什么?
答案 0 :(得分:1)
您只需要在阻止CV之前检查您的矢量是否为空。这样的事情:
while (running) {
std::unique_lock<std::mutex> lock(mutex);
while (tasks_.empty()) // <-- this is important
update_condition_.wait(lock);
// handle tasks_
}
如果您的体系结构允许它(即,如果您在处理任务时不需要保持锁定),您可能还需要在处理任务之前尽快解锁互斥锁,以便生产者可以在不执行任务的情况下执行更多任务阻塞。也许用临时的tasks_
向量交换,然后解锁互斥锁,然后才开始处理临时向量中的任务:
while (running) {
std::unique_lock<std::mutex> lock(mutex);
while (tasks_.empty())
update_condition_.wait(lock);
std::vector<task> localTasks;
localTasks.swap(tasks_);
lock.unlock(); // <-- release the lock early
// handle localTasks
}
编辑啊现在我意识到这并不适合您的情况,因为您的邮件不是直接在tasks_
中,而是在tasks_.results
中。虽然你得到了我的一般想法,但使用它将需要在代码中更改结构(例如,展平你的任务/结果,并且总是有一个与单个结果相关联的cmd)。
答案 1 :(得分:0)
我在同样的情况下以下列方式行事
std::vector< ... > temp;
mutex.lock();
temp.swap( results );
mutex.unlock();
for(result r: temp ){
...
}
一点开销就占了一席之地,但一般来说整个代码更具可重复性,如果计算量很大,那么复制的时间就会变为零(对不起英语 - 对我来说不是原生的)))