浅析互斥锁与条件变量的使用

来源:互联网 发布:淘宝商品如何推广 编辑:程序博客网 时间:2024/05/22 11:59
pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。
几点说明:
    1.pthread_cond_wait() 必须与pthread_mutex 配套使用。

    2.pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。

pthread_cond_wait()用法:

    ptread_mutex_lock(&mut);

    ......
    pthread_cond_wait(&cond, &mut);
    ...... 

    pthread_mutex_unlock(&mut);

    pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成立,我们就进入阻塞,但是进入阻塞期间,如果条件变量恰好改变了的话,我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁(它内部有自己维护一个队列),使得其他线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又会自动给mutex加锁,所以最后需要一个pthread_mutex_unlock()函数来解锁。

    pthread_cond_signal(pthread_cond_t *cond)函数是用来在条件满足时,给正在等待的对象发送信息,表示唤醒该变量,一般和pthread_cond_wait函数联合使用,当它接收到signal发来的信号后,就再次锁住mutex,一旦pthread_cond_wait锁定了互斥对象,那么它将返回并允许wait的线程继续执行。
    下面举一个例子,类似生产者与消费者模型。线程池中的工作线程(线程1)用来处理消息,消息线程(线程2)为工作队列添加消息。
工作线程:
void *
_xxx_worker_thread_routine (
                void *arg)
{
    THREAD_POOL *pThreadPool = (THREAD_POOL *)arg;
    THREAD_WORKER *pWorker;
        
    while (1)
    {
        pthread_mutex_lock(&(pThreadPool->workerQueueLock));
        
        if (pThreadPool->shutdownFlag)
        {
            pthread_mutex_unlock(&(pThreadPool->workerQueueLock));
                        
            pthread_exit (NULL);
        }


        while (pThreadPool->pWorkerQueue == NULL)
            pthread_cond_wait(&(pThreadPool->workerQueueCond), &(pThreadPool->workerQueueLock));
        
        pWorker = pThreadPool->pWorkerQueue;
        pThreadPool->pWorkerQueue = pWorker->pNext;
        
        pthread_mutex_unlock(&(pThreadPool->workerQueueLock));


        if(pWorker->removeFlag == 0)
            (*(pWorker->handler))(pWorker->arg1, pWorker->arg2);
        
        free (pWorker);
    }


    pthread_exit (NULL);
}


消息线程:
int
_xxx_add_worker (
                THREAD_POOL *pThreadPool,
                const _PROC_FUNC handler,
                const UINT16 arg1,
                const UINT16 arg2)
{
    THREAD_WORKER *pNewWorker, *pWorker;


    pNewWorker = (THREAD_WORKER *)malloc(sizeof (THREAD_WORKER));
    if(pNewWorker == NULL)
    {
        return -1;
    }
    
    pNewWorker->handler     = handler;
    pNewWorker->ponId       = arg1;
    pNewWorker->onuId       = arg2;
    pNewWorker->removeFlag  = 0;
    pNewWorker->pNext       = NULL;


    pthread_mutex_lock (&(pThreadPool->workerQueueLock));


    pWorker = pThreadPool->pWorkerQueue;
    if (pWorker != NULL)
    {
        while (pWorker->pNext != NULL)
            pWorker = pWorker->pNext;
        
        pWorker->pNext = pNewWorker;
    }
    else
    {
        pThreadPool->pWorkerQueue = pNewWorker;
    }


    pthread_mutex_unlock (&(pThreadPool->workerQueueLock));


    pthread_cond_signal (&(pThreadPool->workerQueueCond));


    return 0;
}
    首先,让我们考虑以下情况:线程1锁定了互斥对象,然而该消息队列恰巧为空的,这一特定线程什么也干不了,
其设计意图是从消息队列中取出结点,但现在却没有结点。因此,它只能:锁定互斥对象,并pthread_cond_wait。
    线程1挂起同时对互斥对象解锁(于是其他线程可以修改消息队列),并等待条件cond发生(这样当它接收到另一个线程的“信号”时,将苏醒)。
    等待条件cond通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗CPU周期,线程将一直睡眠,直到特定条件发生。
    假设另一个线程(线程2)锁定了mutex并对工作队列添加了一项。在对互斥对象解锁后,线程2会立即调用函数pthread_cond_broadcast(&cond)或者pthread_cond_signal(&cond)。此操作之后,线程2将使所有等待cond条件变量的线程立即苏醒。
   线程1苏醒后pthread_cond_wait执行最后一个操作:重新锁定mutex,一旦它锁定了互斥对象,pthread_cond_wait将返回并允许线程1继续执行。
   此时,它可以马上检查消息队列,取出消息执行处理。处理完成之后退出了函数。并调用pthread_mutex_unlock进行mutex的解锁。

0 0
原创粉丝点击