我正在尝试使用pthread实现基于队列的worker。但我对pthread_cond_wait()
有一些疑惑。
class Worker {
private:
pthread_t thread;
vector<int> queue;
bool stop;
pthread_mutex_t mutex;
pthread_cond_t cond;
public:
Worker() {
stop = false;
if (pthread_mutex_init(&mutex, NULL) != 0)
{
printf("\n mutex init failed\n");
}
if(pthread_cond_init(&cond,NULL) != 0){
printf("\n cond init failed\n");
}
}
~Worker() {
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
void interrupt(){
printf("Going to inturrupt\n");
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond); //broadcast also doesn't work
pthread_mutex_unlock(&mutex);
printf("inturrupted \n");
}
void condition_lock(){
pthread_mutex_lock(&mutex);
while(queue.size() == 0){
printf("Entering conditional lock\n");
pthread_cond_wait(&cond,&mutex);
}
pthread_mutex_unlock(&mutex);
}
void *run(){
printf("run\n");
while(!stop){
printf("Going for condition lock");
printf("size: %d\n",queue.size());
condition_lock();
printf("Exit from condition lock");
while(queue.size() > 0){
printf("item: %d\n",queue[0]);
queue.pop_back();
}
}
pthread_exit(NULL);
}
void push(int value){
pthread_mutex_lock(&mutex);
queue.push_back(value);
pthread_mutex_unlock(&mutex);
}
void join(){
void *status;
pthread_join(thread,&status);
}
static void *run_helper(void* context){
return ((Worker *)context)->run();
}
void stop_thread(){
stop = true;
interrupt();
}
void start_thread(Worker worker){
stop = false;
int status = pthread_create(&thread,NULL,run_helper,&worker);
}
};
int main(){
Worker worker;
worker.start_thread(worker);
usleep(500000);
for(int i=0;i<5;i++){
worker.push(i);
worker.interrupt();
usleep(500000);
}
worker.stop_thread();
worker.join();
printf("Thread exit\n");
return 0;
}
run
Going for condition locksize: 0
Entering conditional lock
Going to inturrupt
inturrupted
Going to inturrupt
inturrupted
Going to inturrupt
inturrupted
Going to inturrupt
inturrupted
Going to inturrupt
inturrupted
Going to inturrupt
inturrupted
永远不会从pthread_cond_wait()
返回。我也不明白pthread_mutex_lock()
方法中void interrupt()
的工作方式,因为它应该已被void condition_lock()
锁定。
我已根据建议更改了代码中的两项更改。
1. use queue.size() == 0 instead of conditional variable.
2. Use mutex lock/unlock during queue.push_back()
答案 0 :(得分:2)
这里的错误:
void start_thread(Worker worker){ // worker passed by value
// thus it is a copy.
stop = false;
int status = pthread_create(&thread,NULL,
run_helper,&worker); // Address of worker passed to thread.
} // worker destroyed here.
您正在按值传递工作人员(因此获得副本)。线程正在针对此副本运行。但是在退出此函数时副本被破坏(因此互斥和cond无效)。
由于this
和worker
应该是相同的。
修复:
void start_thread(){
stop = false;
int status = pthread_create(&thread, NULL, run_helper, this);
}
这是错误的:
void condition_lock(bool condition){
pthread_mutex_lock(&mutex);
if(condition){
// ^^^^ Should be while(<validate some invariant>)
printf("Entering conditional lock\n");
pthread_cond_wait(&cond,&mutex);
}
pthread_mutex_unlock(&mutex);
}
你真的想通过这里传递一个功能。因此,条件变量可以在每次复活时验证condition
。
您正在改变对象的状态而不需要锁定。
void push(int value){
queue.push_back(value);
}
这个类有两个线程。每当修改状态时,您需要获得锁定。这是通过几种方法完成的(甚至修饰stop
应该在锁定下完成。)
从技术上讲,这不是C函数回调的有效目标。
static void *run_helper(void* context){
return ((Worker *)context)->run();
}
C不知道C ++ ABI。 pthreads是一个C库,因此作为回调可以传递的唯一有效指针是C函数。
我也不明白pthread_mutex_lock()在void interrupt()中是如何工作的
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex); The call to wait releases the lock
on the mutex. When the thread is woken
up it must reaquire the lock before
the thread exits the call pthread_cond_wait()
This allows another thread to lock the
mutex modify state then call the signal
mrthod before releasing the lock.
this allows interupt() to run as expected.
注意:仅仅因为你调用signal并不意味着另一个线程被立即安排执行(它只是变得可用)。您的代码处于如此紧凑的循环中,这可能是一个问题,需要锁定它才能退出pthread_cond_wait()
函数。
虽然我留下了仍然需要为你完成的无聊修复。您必须检查所有库调用的结果,以验证它们是否有效。如果它们不起作用,那么至少你能做的就是抛出异常。
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
#include <iostream>
using namespace std;
// LA: Callback must by C function.
extern "C" void *run_helper(void* context);
class Worker {
private:
pthread_t thread;
vector<int> queue;
bool stop;
pthread_mutex_t mutex;
pthread_cond_t cond;
public:
Worker() {
stop = false;
if (pthread_mutex_init(&mutex, NULL) != 0)
{
printf("\n mutex init failed\n");
}
if(pthread_cond_init(&cond,NULL) != 0){
printf("\n cond init failed\n");
}
}
~Worker() {
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
void interrupt(){
printf("Going to inturrupt\n");
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond); //broadcast also doesn't work
pthread_mutex_unlock(&mutex);
printf("inturrupted \n");
}
void *run(){
printf("run\n");
pthread_mutex_lock(&mutex);
while(!stop){
printf("Going for condition lock\n");
printf("size: %lu\n",queue.size());
// LA: Moved condition_lock() inline.
// This was because we needed the lock around
// accessing the state after the wait
// LA: Check queue size and if we are stopped after being woken
while(queue.size() == 0 && !stop){
printf("Entering conditional lock\n");
pthread_cond_wait(&cond,&mutex);
}
printf("Exit from condition lock\n");
while(queue.size() > 0){
printf("item: %d\n",queue[0]);
queue.pop_back();
}
}
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
void push(int value){
// LA: All state mutation needs to be guarded.
pthread_mutex_lock(&mutex);
queue.push_back(value);
pthread_mutex_unlock(&mutex);
}
void join(){
void *status;
pthread_join(thread,&status);
}
void stop_thread(){
// LA: All state mutation needs to be guarded.
pthread_mutex_lock(&mutex);
stop = true;
pthread_mutex_unlock(&mutex);
interrupt();
}
void start_thread(){
int status = pthread_create(&thread,NULL,run_helper,this);
}
};
extern "C" void *run_helper(void* context){
return ((Worker *)context)->run();
}
int main(){
Worker worker;
worker.start_thread();
usleep(500000);
for(int i=0;i<5;i++){
worker.push(i);
worker.interrupt();
usleep(500000);
}
worker.stop_thread();
worker.join();
printf("Thread exit\n");
return 0;
}
答案 1 :(得分:-1)
我也不了解
pthread_mutex_lock()
void interrupt()
的工作原理。
不是。它导致了僵局。
它应该已被
void condition_lock()
锁定。
是的。这就是原因:
它永远不会从
pthread_cond_wait()
返回。