我有一个带有“manager”线程的简单应用程序,它产生了十个简单的“worker”线程。我希望所有“worker”线程都阻塞相同的条件变量(即:condvar),并且我想通过pthread_cond_broadcast调用手动发信号通知所有10个线程同时唤醒。
对于我的应用程序,线程可能会出现错误情况并提前终止,因此有可能并非所有十个线程都进入同步点。
一个简单的机制是创建一个pthread_barrier_t并让所有十个线程调用pthread_barrier_wait,当所有十个线程完成此调用时,它们都可以继续执行。但是,这将要求线程能够修改屏障解除锁定所需的线程数。我不知道这是否可以安全修改。
此外,我想保证所有仍然工作的线程不会像屏障一样自动启动,我想用pthread_cond_broadcast调用手动启动它们。在进行广播呼叫之前,我如何保证所有仍然活着的线程(理想情况下是10个)在condvar上被阻塞?
谢谢!
答案 0 :(得分:2)
以下显示了一种方法,使用条件变量和一些其他变量;虽然可能有更好的方法。评论应该显示它是如何工作的。当然,您必须根据自己的实际情况进行修改;例如,可能涉及循环,等等。
int activeThreads = 0; /* number of threads currently going */
int waitingThreads = 0; /* number of threads waiting on the condition */
int readyFlag = 0; /* flag to tell the threads to proceed when signaled */
pthread_cond_t cond; /* condition to wait on / signal */
pthread_mutex_t mtx; /* mutex for the above */
pthread_cond_t condWaiting; /* EDIT: additional condition variable to signal
* when each thread starts waiting */
void *threadFunc(void *arg)
{
/* Edit: Rather than incrementing 'activeThreads' here, it should be done
* in the main thread when each thread is created (to avoid a race) */
/* ...do stuff... */
/* When the threads should wait, do this (they wait for 'readyFlag' to be
* set, but also adjust the waiting thread count so the main thread can
* determine whether to broadcast) */
pthread_mutex_lock(&mtx);
if (readyFlag == 0) {
waitingThreads++;
do {
pthread_cond_signal(&condWaiting); /* EDIT: signal the main thread when
* a thread begins waiting */
pthread_cond_wait(&cond,&mtx);
} while (readyFlag == 0);
waitingThreads--;
}
pthread_mutex_unlock(&mtx);
/* ...more stuff... */
/* When threads terminate, they decrement the active thread count */
pthread_mutex_lock(&mtx);
activeThreads--;
pthread_cond_signal(&condWaiting); /* EDIT: also signal the main thread
* when a thread exits to make it
* recheck the waiting thread count if
* waiting for all threads to wait */
pthread_mutex_unlock(&mtx);
return NULL;
}
int main(int argc, char *argv[])
{
/* Edit: Showing some code to initialize the mutex, condition variable(s),
* etc., and create some threads -- modify as needed */
pthread_mutex_init(&mtx,NULL);
pthread_cond_init(&cond,NULL);
pthread_cond_init(&condWaiting,NULL); /* EDIT: if main thread should block
* until all threads are waiting */
activeThreads = waitingThreads = readyFlag = 0;
/* Edit: Increment 'activeThreads' here rather than in the thread function,
* to avoid a race (if the main thread started waiting for the others
* when not all had incremented the count yet, the main thread might end
* up waiting for fewer threads to be ready -- though it's unlikely */
#define NUM_THREADS 10
pthread_t workers[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
/* should use appropriate thread attributes */
if (pthread_create(&workers[i],NULL,threadFunc,NULL) == 0)
activeThreads++;
}
/* ...do stuff... */
/* Set 'readyFlag' and do condition broadcast IF all threads are waiting,
* or just carry on if they aren't */
pthread_mutex_lock(&mtx);
if ((activeThreads != 0) && (activeThreads == waitingThreads)) {
readyFlag = 1;
pthread_cond_broadcast(&cond);
}
pthread_mutex_unlock(&mtx);
/* EDIT: OR.. to wait until all threads are waiting and then broadcast, do
* this instead: */
pthread_mutex_lock(&mtx);
while (waitingThreads < activeThreads) { /* wait on 'condWaiting' until all
* active threads are waiting */
pthread_cond_wait(&condWaiting,&mtx);
}
if (waitingThreads != 0) {
readyFlag = 1;
pthread_cond_broadcast(&cond);
}
pthread_mutex_unlock(&mtx);
/* ...more stuff... */
/* If needed, you can clear the flag when NO threads are waiting.. */
pthread_mutex_lock(&mtx);
if (waitingThreads == 0)
readyFlag = 0;
pthread_mutex_unlock(&mtx);
/* ...even more stuff... */
return 0;
}
但是,我想补充一点,我没有看到什么时候有充分理由这样做,而不仅仅是以更直接的方式保护资源。
编辑:在代码中添加了一些内容,显示了第二个条件变量,用于让主线程等待所有工作者准备就绪。更改的部分在注释中标有“编辑:”,如果不需要则可以省略。我还通过将activeThreads
的增量移出线程函数来更正了竞争条件,并显示了互斥锁的初始化等(没有错误处理)。
答案 1 :(得分:1)
一般来说,你应该在工作准备就绪时设置条件变量(和它的相关标志) - 通常不需要等待线程在条件var上阻塞。如果他们已经迟到了,他们会注意到该标志已经设置好了,而且没有阻止。
但是如果你确实需要等到所有工作线程都处于他们对条件var的隐藏之处,你可以使用条件变量的组合 - 一个跟踪多少线程的变量#39准备好了#39;另一个触发他们开始工作。一些peudo代码:
// manager thread thread
pthread_cond_t pseudo_barrier;
pthread_cond_t pseudo_barrier_complete_cond;
pthread_mutex_t pseudo_barrier_mux;
int pseudo_barrier_counter = NUM_THREADS;
int pseudo_barrier_complete_flag = 0;
void thread_manager(void)
{
pthread_cond_init( &pseudo_barrier, NULL);
pthread_cond_init( &pseudo_barrier_complete_cond, NULL);
pthread_mutex_init( &pseudo_barrier_mux, NULL);
for (int i = 0 ; i < NUM_THREADS; ++i) {
pthread_create( /*... */);
}
// wait for threads to 'stage'
pthread_mutex_lock( &pseudo_barrier_mux);
while (pseudo_barrier_counter != 0) {
pthread_cond_wait( &pseudo_barrier, &pseudo_barrier_mux);
}
pthread_mutex_unlock( &pseudo_barrier_mux);
// at this point, all threads have either bailed out or are waiting to go
// let 'em rip
pthread_mutex_lock( &pseudo_barrier_mux);
pseudo_barrier_complete_flag = 1;
pthread_mutex_unlock( &pseudo_barrier_mux);
pthread_cond_broadcast( &pseudo_barrier_complete_cond);
// do whatever else the manager thread needs to do...
}
// worker threads
void* worker_thread(void* context)
{
int error_result = 0;
// whatever initialization...
// if this thread is going to bail out due to an error, it needs to
// set the `error_result` value appropriately and still drop into the
// following code
// let the manager know that this thread is waiting (or isn't going to participate)
pthread_mutex_lock( &pseudo_barrier_mux);
--pseudo_barrier_counter;
if (pseudo_barrier_counter == 0) {
// all other threads are accounted for, let the manager know we're ready
pthread_cond_signal( &pseudo_barrier);
}
// if this thread isn't going to contine because of some error, it's already
// accounted for that fact in the `my_barrier_count`, so we can return here
// without preventing the pseudo-barrier from being met.
if (some_error_occurred) {
pthread_mutex_lock( &pseudo_barrier_mux);
return NULL;
}
// NOTE: we're still holding pseudo_barrier_mux, so the master thread is still
// blocked, even if we've signaled it - it'll jhave to wait until this
// thread is blocking on `pseudo_barrier_complete_cond`
while (!pseudo_barrier_complete_flag) {
pthread_cond_wait( &pseudo_barrier_complete_cond, &pseudo_barrier_mux);
}
pthread_mutex_unlock( &pseudo_barrier_mux);
// do the work...
}
当然,应该清除所呈现的伪代码以供任何实际使用(包括错误处理),可能将所有支持条件变量,互斥和标志打包到结构中