多线程死锁 - 生产者&使用pthread lib的客户模块

时间:2013-03-28 11:14:23

标签: multithreading pthreads deadlock

最近我正在研究pthread多线程lib并做一些例子。

我尝试编写一个Producer-Customer模块:有一个存储Producer产品的队列,可以由客户获取。

我将队列MAX-SIZE设置为20.当队列已满时,Producer线程将等待,直到Customer线程消耗他可以开始生成的Producer线程中的一个和nofity。与队列为空时的Customer相同,Customer将等到Producer线程生成新线程并通知他。 : - )

我将Customer线程设置为比生成更快,它在日志输出中的效果非常好。但是,当我设置Producer线程消耗的速度快于消耗时,它最终会导致死锁: - (

我不知道原因,是否有人可以阅读我的代码并给我一些提示或如何修改代码?

谢谢!

#include "commons.h"

typedef struct tagNode {
    struct tagNode *pNext;
    char *pContent;
}NodeSt, *PNodeSt;

typedef struct {
    size_t  mNodeNum;
    size_t  mNodeIdx;
    PNodeSt mRootNode;
}WorkQueue;

#define WORK_QUEUE_MAX 20

static pthread_cond_t  g_adder_cond  = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t g_adder_mutex = PTHREAD_MUTEX_INITIALIZER;
static WorkQueue g_work_queue = {0};
//------------------------------------------------------------------------
void *customer_thread_runFunc(void *usrdat){
    for( ; ; ) {
pthread_mutex_lock(&g_adder_mutex);{
        while( g_work_queue.mNodeNum == 0 ) {
            pthread_cond_wait(&g_adder_cond, &g_adder_mutex);
        }
/********************** CONSUME NEW PRODUCT ***********************/
        g_work_queue.mNodeNum --;

        if( g_work_queue.mRootNode->pNext != NULL ) {
            PNodeSt pTempNode = g_work_queue.mRootNode->pNext;
            free( g_work_queue.mRootNode->pContent );
            free( g_work_queue.mRootNode );
            g_work_queue.mRootNode = pTempNode;
        } else {
            free( g_work_queue.mRootNode->pContent );
            free( g_work_queue.mRootNode );
            g_work_queue.mRootNode = NULL;
        }
/********************** CONSUME PRODUCT END ***********************/
        // Nofity Producer Thread
        pthread_cond_signal(&g_adder_cond);
}pthread_mutex_unlock(&g_adder_mutex);

        // PAUSE FOR 300ms
        usleep(300); 
    }
    return NULL;
}
//------------------------------------------------------------------------
void *productor_thread_runFunc( void *usrdat ) {
    for( ; ; ) {
pthread_mutex_lock(&g_adder_mutex); {
        char tempStr[64];
        PNodeSt pNodeSt = g_work_queue.mRootNode;

        while( g_work_queue.mNodeNum >= WORK_QUEUE_MAX ) {
            pthread_cond_wait(&g_adder_cond, &g_adder_mutex);
        }

/********************** PRODUCE NEW PRODUCT ***********************/
        g_work_queue.mNodeNum ++;
        g_work_queue.mNodeIdx ++;

        if( pNodeSt != NULL ) {
            for( ; pNodeSt->pNext != NULL; pNodeSt = pNodeSt->pNext );
            pNodeSt->pNext = malloc(sizeof(NodeSt));
            memset(pNodeSt->pNext, 0, sizeof(NodeSt));
            sprintf( tempStr, "production id: %d", g_work_queue.mNodeIdx);
            pNodeSt->pNext->pContent = strdup(tempStr);
        } else {
            g_work_queue.mRootNode = malloc(sizeof(NodeSt));
            memset(g_work_queue.mRootNode, 0, sizeof(NodeSt));
            sprintf( tempStr, "production id: %d", g_work_queue.mNodeIdx);
            g_work_queue.mRootNode->pContent = strdup(tempStr);
        }
/********************** PRODUCE PRODUCT END ***********************/
        // Nofity Customer Thread
        pthread_cond_signal(&g_adder_cond);
}pthread_mutex_unlock(&g_adder_mutex);

        // PAUSE FOR 150ms, faster than Customer Thread
        usleep(150); 
    }
    return NULL;
}
//------------------------------------------------------------------------
int main(void) {

    pthread_t pt1, pt3;
    pthread_attr_t attr;

    pthread_attr_init(&attr);
    pthread_create(&pt1, &attr, customer_thread_runFunc, NULL);
    pthread_create(&pt3, &attr, productor_thread_runFunc, NULL);
    pthread_join(pt1, NULL);
    pthread_join(pt3, NULL);

    printf("MAIN - main thread finish!\n");
    return EXIT_SUCCESS;
}

1 个答案:

答案 0 :(得分:0)

您的制作人正在等待与您的消费者相同的条件?这绝对是一个麻烦的来源。从概念上考虑您的代码。生产者在“生产”之前需要什么先决条件?如你所述,缓冲区需要有空间。

我没有详细查看,但您可能需要一个生产者使用的附加条件变量(与消费者不同)。只有队列已满,生产者才会等待。每次成功从队列中检索某些东西时,消费者都会发出信号。

编辑:阅读pthread lib的文档,两个条件可以使用一个互斥锁

PSEUDOCODE的想法:)

Mutex mqueue
Condition cprod, ccons

produce()
  mqueue.lock
  while the queue is full
     cprod.wait(mqueue)
  end
  do the production on queue
  mcons.signal
  mqueue.unlock
end produce

consume()
  mqueue.lock
  while the queue is empty
      ccons.wait(mqueue)
  end
  do the consumption on the queue
  cprod.signal
  mqueue.unlock
end consume

最好在锁定时发出信号。在这里,我不认为订单会有所作为。