在我的程序中,主线程创建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
}
}
问题在于,有时我的主线程不会卡在条件变量中以等待工作者,这有时会导致分段错误。是否有任何竞争条件我无法看到并导致此问题?
答案 0 :(得分:0)
您的代码中存在明显的并发问题。为简单起见,假设我们有2名工人,还剩2个工作岗位。可能发生以下情况:
myArray
,而它会被清理,或者其他任何可能出错的地方。 因此,您有两次比赛和/或segfalts的机会:在父级和仍然活跃的工作者中,忙于一项不存在的任务。
我认为如果你开始循环获取锁定,检查是否还有剩余的作业,并且预先减少作业数量,那么工作循环会更安全,这样同伴就会知道真正剩下的是什么:
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)
上建立父循环
答案 1 :(得分:0)
我用你的代码看到的唯一麻烦就是每个工人都会疯狂地运行(如果你的处理不涉及定时器/ IO,可能是100%的CPU),并且一旦没有更多的话就不会终止作业。
在实际案例中,您的工作人员应该从队列或类似的东西中获取作业请求,并在没有可用的作业处理时阻止(等待可用作业或某些终止命令)。
我看到这里没有竞争条件。我看到的唯一错误是工作计数的系统性减少。它很快就会变成消极的,如果你认为它不能,那可能会引发各种各样的麻烦。为避免这种情况,只需在递减计数器之前将其计数为零。