10

为了理解pthread条件变量的代码,我写了自己的版本。它看起来正确吗?我在一个程序中使用它,它的工作,但工作速度惊人的快。最初程序大约需要 2.5 秒,而使用我的条件变量版本只需要 0.8 秒,并且程序的输出也是正确的。但是,我不确定我的实现是否正确。

struct cond_node_t
{
    sem_t s;
    cond_node_t * next;
};

struct cond_t
{
    cond_node_t * q;                // Linked List
    pthread_mutex_t qm;                 // Lock for the Linked List
};

int my_pthread_cond_init( cond_t * cond )
{
    cond->q = NULL;
    pthread_mutex_init( &(cond->qm), NULL );
}

int my_pthread_cond_wait( cond_t* cond, pthread_mutex_t* mutex )
{
    cond_node_t * self;

    pthread_mutex_lock(&(cond->qm));
    self = (cond_node_t*)calloc( 1, sizeof(cond_node_t) );
    self->next = cond->q;
    cond->q = self;
    sem_init( &self->s, 0, 0 );
    pthread_mutex_unlock(&(cond->qm));

    pthread_mutex_unlock(mutex);
    sem_wait( &self->s );
    free( self ); // Free the node
    pthread_mutex_lock(mutex);
}

int my_pthread_cond_signal( cond_t * cond )
{
    pthread_mutex_lock(&(cond->qm));
    if (cond->q != NULL) 
    {
        sem_post(&(cond->q->s));
        cond->q = cond->q->next;
    }
    pthread_mutex_unlock(&(cond->qm));
}

int my_pthread_cond_broadcast( cond_t * cond )
{
    pthread_mutex_lock(&(cond->qm));
    while ( cond->q != NULL) 
    {
        sem_post( &(cond->q->s) );
        cond->q = cond->q->next;
    }
    pthread_mutex_unlock(&(cond->qm));
}
4

3 回答 3

5

除了缺少返回值检查之外,还有一些应该修复的问题:

  • sem_destroy不叫。
  • 信号/广播cond_node_t在唤醒目标线程后触摸,可能导致 use-after-free。

进一步评论:

  • 省略的销毁操作可能需要更改其他操作,因此当 POSIX 说它应该是安全的时,销毁条件变量是安全的。不支持销毁或对何时调用它施加更严格的限制将简化事情。
  • 生产实现将处理线程取消。
  • 退出等待(例如线程取消和pthread_cond_timedwait超时所需的)可能会导致并发症。
  • 您的实现在用户空间中对线程进行排队,出于性能原因,这是在某些生产实现中完成的;我不明白为什么。
  • 您的实现始终按 LIFO 顺序对线程进行排队。这通常更快(例如由于缓存效应),但可能导致饥饿。生产实施有时可能会使用先进先出顺序来避免饥饿。
于 2012-06-12T23:14:31.940 回答
4

基本上你的策略看起来不错,但是你有一个主要的危险,一些未定义的行为和一个挑剔的选择:

  • 您没有检查 POSIX 函数的返回值。特别sem_wait是可中断的,因此在重负载或运气不好的情况下,您的线程将被虚假唤醒。你必须仔细捕捉所有这些
  • 您的任何函数都没有返回值。如果某天函数的某些用户决定使用返回值,这是未定义的行为。仔细分析条件函数允许返回的错误代码并做到这一点。
  • 不要放弃malloccalloc

编辑:实际上,您根本不需要malloc/ free。局部变量也可以。

于 2012-06-12T16:50:17.093 回答
3

您似乎不尊重此要求:

这些函数以原子方式释放互斥体并导致调用线程阻塞条件变量 cond;atomically 这里的意思是“原子地相对于另一个线程访问互斥锁,然后是条件变量”。也就是说,如果另一个线程能够在即将阻塞的线程释放后获得互斥锁,那么该线程中对 pthread_cond_broadcast() 或 pthread_cond_signal() 的后续调用将表现得就好像它是在 about-- to-block 线程已阻塞。

你解锁然后等待。另一个线程可以在这些操作之间做很多事情。

PS我不确定自己是否正确解释了这一段,请随时指出我的错误。

于 2012-06-12T17:55:20.083 回答