父级不等待工作线程完成任务
Parent does not wait for worker threads to complete task
在我的程序中,主线程创建 4 个(或更多)工作线程。在某些时候,父线程(主线程)必须等待工作线程才能完成一些计算。线程在无限循环上运行,所以我无法使用 pthread_join(.., ..)
POSIX 函数来等待工作线程完成。所以我正在使用全局计数器和条件变量。
主线程代码
unsigned jobs = 0; // global variable
// global mutex and cv. They get initialised in my main.
pthread_mutex_t counter_mutex;
pthread_cond_t counter_cv;
static void process(..){
jobs = myArray.size();
// I am using a function here that broadcasts a cv in order to
// wake up the workers
pthread_mutex_lock(&counter_mutex); // lock counter
while (jobs > 0){
pthread_cond_wait(&counter_cv, &counter_mutex); // PARENT SHOULD GET STUCK HERE TILL WORKERS ARE DONE
}
// cout << "Workers are done" << endl;
pthread_mutex_unlock(&counter_mutex); // unlock counter
}
工人守则
extern unsigned jobs;
extern pthread_mutex_t counter_mutex;
extern pthread_cond_t counter_cv;
void *run() {
for (int i = 0;; i++) {
// do some calculations here
pthread_mutex_lock(&counter_mutex); // lock counter
jobs--;
if (jobs == 0){
pthread_cond_signal(&counter_cv);
cout << "All jobs are done" << endl;
}
pthread_mutex_unlock(&counter_mutex); // unlock counter
}
}
问题是有时我的主线程不会卡在条件变量上以等待工作线程,这有时会导致分段错误。是否存在任何我看不到的竞争条件并导致此问题?
代码中存在明显的并发问题。为了简单起见,假设我们有 2 名工人,还剩下 2 份工作。 可能会发生以下情况:
- 作业计数设置为 2
- 父级被困在等待条件变量
- 两个工作线程在他们的无限循环中开始处理迭代
- 无论出于何种原因,第一个工作线程都会更快地完成其任务,并执行锁定的作业计数更新。作业计数递减为 1,因此父项将等待并释放锁。 然后,工作线程开始其下一次迭代。不幸的是,实际上没有工作了(1 个已经完成,1 个目前由第二个工人执行)。但它启动了一个假设的工作(也许解决同时被删除或超出范围的数组元素?
- 第二个工作线程完成其作业,作业计数递减为 0,导致父工作线程被唤醒,按住锁,退出循环并解锁。
- 您注意到,虽然父级认为它已经结束,但第一个工作线程仍在处理假设的任务,也许试图访问
myArray
而它被清理干净,或者其他任何可能出错的地方。
因此,您有两个种族和/或赛段的机会:在父母和仍然活跃的工人中,忙于一项不存在的任务。
我认为,如果您启动循环以获取锁定,检查是否还有剩余的作业,并预先减少作业计数,以便同行工作人员知道真正剩下的内容,那么工作线程循环会更安全:
void *run() {
for (int i = 0;; i++) {
pthread_mutex_lock(&counter_mutex); // lock counter
if (jobs == 0){
pthread_cond_signal(&counter_cv);
cout << "All jobs are done" << endl;
}
else jobs--;
pthread_mutex_unlock(&counter_mutex); // unlock counter
// do some calculations here
}
}
优点是,工人只有在真正剩下工作时才工作。 那么唯一的麻烦是,父母被第一个失业的工人唤醒了。然而,其他工人可能仍在运行。
例如,如果这是一个问题,您还可以维护一个仍然处于活动状态的作业计数器,并使父循环打开(jobs>0 || active_jobs>0)
我在你的代码中看到的唯一问题是每个工作线程都会疯狂运行(如果你的处理不涉及计时器/IO,可能会以 100% CPU 运行),并且一旦没有更多的作业就不会终止。
在实际情况下,您的工作人员应该从队列或类似内容中获取作业请求,并在没有可用作业要处理时阻止(等待可用作业或某些终止命令)。
我在这里看不到竞争条件。我看到的唯一错误是作业计数的系统性减少。它很快就会变成负面的,如果你假设它不能,这可能会造成各种各样的麻烦。为避免这种情况,只需在递减计数器之前将其测试为零即可。
- 在可拆卸线程完成操作时取消分配内存
- std::thread从函数启动,无需等待线程完成
- 线程完成后在另一线程中调用方法
- 有哪些方法可以让主线程等待所有工作线程完成
- 如何等待多个线程完成(使用 c++11 线程)
- 解决死锁问题,在主线程中等待多个工作线程完成 (C++11)
- 线程池 - 如何在成员线程完成后删除工作线程(此)
- 在 C++11 中从多个线程获取值,而无需等待任何给定线程完成执行
- 同时运行不同的线程,而无需等待其他线程完成
- 为什么一个omp线程完成,而另一个却不能完成
- OpenMP 构造,在至少 1 个线程完成后立即继续执行
- 如何处理任务,同时剩余的线程完成工作
- 期货是检查单个线程完成情况的安全方法吗
- JNI - Java 在本机线程完成执行之前退出
- 在退出进程时等待线程完成
- 父级不等待工作线程完成任务
- 一旦另一个线程完成,就会生成一个新线程
- 如何显式线程化任务
- 父线程 join():块直到子线程完成
- 不想让主线程等待提升线程完成?