通过 pthread_cond_broadcast 触发多个 pthread
Trigger multiple pthreads by pthread_cond_broadcast
由于带有 pthread_cond_broadcast
唤醒的 pthreads 的例子很少,我写了一个,但不确定这是否正确同步以及实现它的方法:
- 是否所有线程共享相同的 c 和 mtx 变量?
- 是否有必要在
pthread_cond_wait
return 时测试是否确实满足某些条件?在我的例子中,每次广播调用都应该恰好唤醒每个线程一次,但其他人不应该这样做。 (我能防止虚假唤醒吗?)
- 尽管异步取消类型,程序当前不会退出。尽管
pthread_cond_wait
是取消点 so,但在示例代码中尝试延迟取消也没有成功。
总的来说它是否像我期望的那样工作。
#include <pthread.h>
#include <iostream>
#include <unistd.h>
struct p_args{
int who;
};
pthread_cond_t c; //share between compilation units
pthread_mutex_t mtx;
void *threadFunc(void *vargs){
//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
struct p_args * args = (struct p_args *) vargs;
while(true){
//wait for trigger one loop
pthread_mutex_lock(&mtx);
pthread_cond_wait(&c, &mtx);
pthread_mutex_unlock(&mtx);
//should be entangled output showing concurrent execution
std::cout << "t " << args->who << std::endl;
/* expensive work */
}
delete args;
}
int main(int argc, char* argv[])
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id[2];
struct p_args *args0 = new p_args();
struct p_args *args1 = new p_args();
args0->who = 0;
args1->who = 1;
pthread_create(&thread_id[0], NULL, threadFunc, args0);
pthread_create(&thread_id[1], NULL, threadFunc, args1);
sleep(3);
pthread_mutex_lock(&mtx);
pthread_cond_broadcast(&c);
pthread_mutex_unlock(&mtx);
sleep(3);//test if thread waits
pthread_cancel(thread_id[0]);
pthread_cancel(thread_id[1]);
pthread_join (thread_id[0], NULL);
pthread_join (thread_id[1], NULL);
//could perform cleanup here
return 0;
}
Regarding exiting deferred:
thread_id[0] exits fine and i am stuck in line `pthread_join (thread_id[1], NULL);`, it says (Exiting) but seems stuck on a lock, with debugger:
<br>
[![enter image description here][2]][2]
<br>
编辑 我想出的最终解决方案:
#include <pthread.h>
#include <iostream>
#include <unistd.h>
struct p_args{
int who;
};
pthread_cond_t c;
pthread_mutex_t mtx;
bool doSome[2];
bool exitFlag;
void *threadFunc(void *vargs){
struct p_args * args = (struct p_args *) vargs;
while(true){
//wait for trigger one loop
pthread_mutex_lock(&mtx);
do {
pthread_cond_wait(&c, &mtx);
if(exitFlag) {
std::cout << "return " << args->who << std::endl;
delete args;
pthread_mutex_unlock(&mtx);
return NULL;
}
} while(doSome == false);
doSome[args->who] = false;
pthread_mutex_unlock(&mtx);
std::cout << "t " << args->who << std::endl;
}
}
int main(int argc, char* argv[])
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id[2];
struct p_args *args0 = new p_args();
struct p_args *args1 = new p_args();
args0->who = 0;
args1->who = 1;
doSome[0] = doSome[1] = true;
exitFlag = false;
pthread_create(&thread_id[0], NULL, threadFunc, args0);
pthread_create(&thread_id[1], NULL, threadFunc, args1);
doSome[0] = doSome[1] = true;
pthread_cond_broadcast(&c);
sleep(3);
doSome[0] = doSome[1] = true;
pthread_cond_broadcast(&c);
sleep(3);
exitFlag = true;
pthread_cond_broadcast(&c);
pthread_join (thread_id[0], NULL);
pthread_join (thread_id[1], NULL);
return 0;
}
- do all threads share the same c and mtx variable?
是的,就像任何其他全局变量一样。您可以从每个线程打印他们的地址以确认它。
- is it necessary upon pthread_cond_wait return to test if some condition is actually met?
是的,所有等待接口都受到虚假唤醒的影响,您始终有责任检查自己的谓词。看documentation还是一本好书。
- the program currently does not exit ...
pthread_cancel
太可怕了,永远不应该使用。真的很难做到正确。如果你想告诉你的线程退出,写一个通知机制 - 将它构建到现有的谓词循环中 - 和 signal/broadcast 以确保所有线程醒来并意识到它是时候死了。
Regarding exiting deferred: thread_id[0] exits fine and i am stuck in line pthread_join (thread_id[1], NULL);, it says (Exiting) but seems stuck on a lock
关于 pthread_cancel
的困难之一是 清理。如果在您持有锁时取消发生,您需要使用 pthread_cleanup_push
来模拟与取消兼容的 RAII 语义。否则,第一个线程可能(在本例中确实)在互斥量仍处于锁定状态时死掉。
在这种情况下,由于取消,第二个线程试图从 pthread_const_wait
退出,但它需要重新获得锁,但不能。
条件变量循环的通常形式是这样的(一本好的参考书应该有类似的内容):
void *thread(void *data)
{
struct Args *args = (struct Args *)data;
/* this lock protects both the exit and work predicates.
* It should probably be part of your argument struct,
* globals are not recommended.
* Error handling omitted for brevity,
* but you should really check the return values.
*/
pthread_mutex_lock(&args->mutex);
while (!exit_predicate(args)) {
while (!work_predicate(args)) {
/* check the return value here too */
pthread_cond_wait(&args->condition, &args->mutex);
}
/* work_predicate() is true and we have the lock */
do_work(args);
}
/* unlock (explicitly) only once.
* If you need to cope with cancellation, you do need
* pthread_cleanup_push/pop instead.
*/
pthread_mutex_unlock(&args->mutex);
return data;
}
您的自定义代码可以直接进入 bool exit_predicate(struct Args*)
、bool work_predicate(struct Args*)
和 void do_work(struct Args*)
。循环结构本身很少需要改动。
由于带有 pthread_cond_broadcast
唤醒的 pthreads 的例子很少,我写了一个,但不确定这是否正确同步以及实现它的方法:
- 是否所有线程共享相同的 c 和 mtx 变量?
- 是否有必要在
pthread_cond_wait
return 时测试是否确实满足某些条件?在我的例子中,每次广播调用都应该恰好唤醒每个线程一次,但其他人不应该这样做。 (我能防止虚假唤醒吗?) - 尽管异步取消类型,程序当前不会退出。尽管
pthread_cond_wait
是取消点 so,但在示例代码中尝试延迟取消也没有成功。
总的来说它是否像我期望的那样工作。
#include <pthread.h>
#include <iostream>
#include <unistd.h>
struct p_args{
int who;
};
pthread_cond_t c; //share between compilation units
pthread_mutex_t mtx;
void *threadFunc(void *vargs){
//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
struct p_args * args = (struct p_args *) vargs;
while(true){
//wait for trigger one loop
pthread_mutex_lock(&mtx);
pthread_cond_wait(&c, &mtx);
pthread_mutex_unlock(&mtx);
//should be entangled output showing concurrent execution
std::cout << "t " << args->who << std::endl;
/* expensive work */
}
delete args;
}
int main(int argc, char* argv[])
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id[2];
struct p_args *args0 = new p_args();
struct p_args *args1 = new p_args();
args0->who = 0;
args1->who = 1;
pthread_create(&thread_id[0], NULL, threadFunc, args0);
pthread_create(&thread_id[1], NULL, threadFunc, args1);
sleep(3);
pthread_mutex_lock(&mtx);
pthread_cond_broadcast(&c);
pthread_mutex_unlock(&mtx);
sleep(3);//test if thread waits
pthread_cancel(thread_id[0]);
pthread_cancel(thread_id[1]);
pthread_join (thread_id[0], NULL);
pthread_join (thread_id[1], NULL);
//could perform cleanup here
return 0;
}
Regarding exiting deferred:
thread_id[0] exits fine and i am stuck in line `pthread_join (thread_id[1], NULL);`, it says (Exiting) but seems stuck on a lock, with debugger:
<br>
[![enter image description here][2]][2]
<br>
编辑 我想出的最终解决方案:
#include <pthread.h>
#include <iostream>
#include <unistd.h>
struct p_args{
int who;
};
pthread_cond_t c;
pthread_mutex_t mtx;
bool doSome[2];
bool exitFlag;
void *threadFunc(void *vargs){
struct p_args * args = (struct p_args *) vargs;
while(true){
//wait for trigger one loop
pthread_mutex_lock(&mtx);
do {
pthread_cond_wait(&c, &mtx);
if(exitFlag) {
std::cout << "return " << args->who << std::endl;
delete args;
pthread_mutex_unlock(&mtx);
return NULL;
}
} while(doSome == false);
doSome[args->who] = false;
pthread_mutex_unlock(&mtx);
std::cout << "t " << args->who << std::endl;
}
}
int main(int argc, char* argv[])
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id[2];
struct p_args *args0 = new p_args();
struct p_args *args1 = new p_args();
args0->who = 0;
args1->who = 1;
doSome[0] = doSome[1] = true;
exitFlag = false;
pthread_create(&thread_id[0], NULL, threadFunc, args0);
pthread_create(&thread_id[1], NULL, threadFunc, args1);
doSome[0] = doSome[1] = true;
pthread_cond_broadcast(&c);
sleep(3);
doSome[0] = doSome[1] = true;
pthread_cond_broadcast(&c);
sleep(3);
exitFlag = true;
pthread_cond_broadcast(&c);
pthread_join (thread_id[0], NULL);
pthread_join (thread_id[1], NULL);
return 0;
}
- do all threads share the same c and mtx variable?
是的,就像任何其他全局变量一样。您可以从每个线程打印他们的地址以确认它。
- is it necessary upon pthread_cond_wait return to test if some condition is actually met?
是的,所有等待接口都受到虚假唤醒的影响,您始终有责任检查自己的谓词。看documentation还是一本好书。
- the program currently does not exit ...
pthread_cancel
太可怕了,永远不应该使用。真的很难做到正确。如果你想告诉你的线程退出,写一个通知机制 - 将它构建到现有的谓词循环中 - 和 signal/broadcast 以确保所有线程醒来并意识到它是时候死了。
Regarding exiting deferred: thread_id[0] exits fine and i am stuck in line pthread_join (thread_id[1], NULL);, it says (Exiting) but seems stuck on a lock
关于 pthread_cancel
的困难之一是 清理。如果在您持有锁时取消发生,您需要使用 pthread_cleanup_push
来模拟与取消兼容的 RAII 语义。否则,第一个线程可能(在本例中确实)在互斥量仍处于锁定状态时死掉。
在这种情况下,由于取消,第二个线程试图从 pthread_const_wait
退出,但它需要重新获得锁,但不能。
条件变量循环的通常形式是这样的(一本好的参考书应该有类似的内容):
void *thread(void *data)
{
struct Args *args = (struct Args *)data;
/* this lock protects both the exit and work predicates.
* It should probably be part of your argument struct,
* globals are not recommended.
* Error handling omitted for brevity,
* but you should really check the return values.
*/
pthread_mutex_lock(&args->mutex);
while (!exit_predicate(args)) {
while (!work_predicate(args)) {
/* check the return value here too */
pthread_cond_wait(&args->condition, &args->mutex);
}
/* work_predicate() is true and we have the lock */
do_work(args);
}
/* unlock (explicitly) only once.
* If you need to cope with cancellation, you do need
* pthread_cleanup_push/pop instead.
*/
pthread_mutex_unlock(&args->mutex);
return data;
}
您的自定义代码可以直接进入 bool exit_predicate(struct Args*)
、bool work_predicate(struct Args*)
和 void do_work(struct Args*)
。循环结构本身很少需要改动。