pthread的条件等待永远不会在c ++中返回

时间:2015-12-11 11:22:57

标签: c++ pthreads

我正在尝试使用pthread实现基于队列的worker。但我对pthread_cond_wait()有一些疑惑。

class Worker

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()

2 个答案:

答案 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无效)。

由于thisworker应该是相同的。

修复:

     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()返回。