相互排斥并不是排他性的

时间:2012-03-09 15:43:22

标签: c pthreads mutex

我有以下代码,它运行在由主线程的init调用启动的2个线程中。一个用于写入设备,一个用于读取。其他线程调用我的应用程序将项添加到队列中。 pop_queue处理所有锁定,push_queue也是如此。每当我修改req r时,我都会锁定它的互斥锁。 q->process是指向write_sectorread_setor之一的函数指针。我需要防止同时调用这两个函数指针,所以我在实际的进程调用中使用了一个互斥锁,但这不起作用。

根据文本程序,我正在对进程函数进行并行调用。考虑到我之前立即锁定并在之后立即解锁,这怎么可能呢?

valgrind --tool=helgrind的以下错误可能有帮助?

==3850== Possible data race during read of size 4 at 0xbea57efc by thread #2
==3850==    at 0x804A290: request_handler (diskdriver.c:239)

第239行是r->state = q->process(*device, &r->sd) +1

void *
request_handler(void *arg)
{
    req *r;
    queue *q = arg;
    int writing = !strcmp(q->name, "write");
    for(;;) {
        /*
         * wait for a request
         */
        pop_queue(q, &r, TRUE);

        /*
         * handle request
         * req r is unattached to any lists, but must lock it's properties incase being redeemed
         */
        printf("Info: driver: (%s) handling req %d\n", q->name, r->id);
        pthread_mutex_lock(&r->lock);

        pthread_mutex_lock(&q->processing);
        r->state = q->process(*device, &r->sd) +1;
        pthread_mutex_unlock(&q->processing);

        /* 
         * if writing, return the SectorDescriptor
         */
         if (writing) {
            printf("Info: driver (write thread) has released a sector descriptor.\n");
            blocking_put_sd(*sd_store, r->sd);
            r->sd = NULL;
        }

        pthread_mutex_unlock(&r->lock);
        pthread_cond_signal(&r->changed);

    }
}

修改

这是读取req属性的另一个位置

int redeem_voucher(Voucher v, SectorDescriptor *sd)
{
    int result;

    if (v == NULL){
        printf("Driver: null voucher redeemed!\n");
        return 0;
    }
    req *r = v;
    pthread_mutex_lock(&r->lock);

    /* if state = 0 job still running/queued */
    while(r->state==0) {
        printf("Driver: blocking for req %d to finish\n", r->id);
        pthread_cond_wait(&r->changed, &r->lock);
    }

    sd = &r->sd;
    result = r->state-1;
    r->sd = NULL;
    r->state = WAIT;
    //printf("Driver: req %d completed\n", r->id);
    pthread_mutex_unlock(&r->lock);
    /*
     * return req to pool
     */    
    push_queue(&pool_q, r);
    return result;
}

编辑2 这是push_和pop_queue函数

int
pop_queue(struct queue *q, req **r, int block)
{
    pthread_mutex_lock(&q->lock);
    while(q->head == NULL) {
        if(block) {
            pthread_cond_wait(&q->wait, &q->lock);
        }
        else {
            pthread_mutex_unlock(&q->lock);
            return FALSE;
        }
    }

    req *got = q->head;
    q->head = got->next;
    got->next = NULL;
    if(!q->head) {
        /* just removed last element */
        q->tail = q->head;
    }

    *r = got;
    pthread_mutex_unlock(&q->lock);
    return TRUE;
}

/*
 * perform a standard linked list insertion to the queue specified
 * handles all required locking and signals any listeners
 * return: int - if insertion was successful
 */
int
push_queue(queue *q, req *r)
{
    /*
     * push never blocks, 
     */
    if(!r || !q)
        return FALSE;

    pthread_mutex_lock(&q->lock);

    if(q->tail) {
        q->tail->next = r;
        q->tail = r;
    }
    else {
        /* was an empty queue */
        q->tail = q->head = r;
    }

    pthread_mutex_unlock(&q->lock);
    pthread_cond_signal(&q->wait);

    return TRUE;
}

2 个答案:

答案 0 :(得分:0)

根据可用信息,似乎可能的另一个线程正在修改*device指向的数据。也许在q->processing互斥锁未被保留的情况下进行修改。

答案 1 :(得分:0)

你的行

pthread_cond_signal(&r->changed);

让我怀疑您还有其他代码也在操纵r指向的结构。在任何情况下,如果你没有人等待那个条件变量就没有多大意义。 (你应该反转解锁和信号线。)

因此,可能您的错误只是在其他地方,您可以同时访问r而无需锁定互斥锁。你没有向我们展示你的其余代码,所以说更多的是更多的猜测工作。