pthread和条件变量

问题描述 投票:0回答:1

我正在关注来自here的pthread教程。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t count_mutex     = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t condition_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  condition_cond  = PTHREAD_COND_INITIALIZER;

void *functionCount1();
void *functionCount2();
int  count = 0;
#define COUNT_DONE  10
#define COUNT_HALT1  3
#define COUNT_HALT2  6

main()
{
   pthread_t thread1, thread2;

   pthread_create( &thread1, NULL, &functionCount1, NULL);
   pthread_create( &thread2, NULL, &functionCount2, NULL);
   pthread_join( thread1, NULL);
   pthread_join( thread2, NULL);

   exit(0);
}

void *functionCount1()
{
   for(;;)
   {
      pthread_mutex_lock( &condition_mutex );
      while( count >= COUNT_HALT1 && count <= COUNT_HALT2 )
      {
         pthread_cond_wait( &condition_cond, &condition_mutex );
      }
      pthread_mutex_unlock( &condition_mutex );

      pthread_mutex_lock( &count_mutex );
      count++;
      printf("Counter value functionCount1: %d\n",count);
      pthread_mutex_unlock( &count_mutex );

      if(count >= COUNT_DONE) return(NULL);
    }
}

void *functionCount2()
{
    for(;;)
    {
       pthread_mutex_lock( &condition_mutex );
       if( count < COUNT_HALT1 || count > COUNT_HALT2 )
       {
          pthread_cond_signal( &condition_cond );
       }
       pthread_mutex_unlock( &condition_mutex );

       pthread_mutex_lock( &count_mutex );
       count++;
       printf("Counter value functionCount2: %d\n",count);
       pthread_mutex_unlock( &count_mutex );

       if(count >= COUNT_DONE) return(NULL);
    }

}

并且作者补充说,functioncount1()停止了值COUNT_HALT1COUNT_HALT2之间的值。

示例输出如下:

Counter value functionCount1: 1
Counter value functionCount1: 2
Counter value functionCount1: 3
Counter value functionCount2: 4
Counter value functionCount2: 5
Counter value functionCount2: 6
Counter value functionCount2: 7
Counter value functionCount1: 8
Counter value functionCount1: 9
Counter value functionCount1: 10
Counter value functionCount2: 11

从我对代码的观察,不应该被functionCount2计算?在functionCount1的while循环中,它在包括3的任何值上调用wait(),这使我认为3应该由functionCount2而不是functionCount1计算。

为什么不是这样?

concurrency pthreads condition-variable
1个回答
1
投票

一个线程可以读取count只有condition_mutex持有(其中count测试对COUNT_HALT1COUNT_HALT)或没有任何互斥量(count测试对COUNT_DONE),而另一个线程可以修改count只持有count_mutex。这种不同步的访问会导致未定义的行为,因为在问题的注释中注明了@EOF,因此代码完全不正确。

也就是说,即使我们在没有其他线程的情况下只运行functionCount1()(这样就不会发生非同步访问),我们仍然希望看到这个输出:

Counter value functionCount1: 1
Counter value functionCount1: 2
Counter value functionCount1: 3

这是因为计数器值在增量之后打印,因此在最后一次迭代中它看到初始计数器值为2,不等待,递增计数器,然后打印新计数器值3。

请注意,在原始代码中,即使忽略非同步访问,functionCount1仍有可能执行从3到4的增量。这是因为在functionCount1之间看到count值为2并决定不等待,并且实际锁定count_mutex,该值可以由另一个线程递增。

要删除对count的非同步访问并修复上一段中引用的种族,你只需完全删除condition_mutex并使用count_mutex代替,保持它在pthread_cond_wait()返回和count的实际增量之间锁定。这是一般模式:当你调用pthread_cond_wait()时锁定的互斥锁应该是保护你正在等待你的条件变量的共享状态的互斥锁(这里,共享状态只是count变量):

void *functionCount1()
{
    for(;;)
    {
        pthread_mutex_lock( &count_mutex );
        while( count >= COUNT_HALT1 && count <= COUNT_HALT2 )
        {   
            pthread_cond_wait( &condition_cond, &count_mutex );
        }

        count++;
        printf("Counter value functionCount1: %d\n",count);

        if (count >= COUNT_DONE)
        {   
            pthread_mutex_unlock( &count_mutex );
            return(NULL);
        }
        pthread_mutex_unlock( &count_mutex );
    }
}

void *functionCount2()
{
    for(;;)
    {
        pthread_mutex_lock( &count_mutex );
        if( count < COUNT_HALT1 || count > COUNT_HALT2 )
        {
            pthread_cond_signal( &condition_cond );
        }

        count++;
        printf("Counter value functionCount2: %d\n",count);

        if (count >= COUNT_DONE)
        {
            pthread_mutex_unlock( &count_mutex );
            return(NULL);
        }
        pthread_mutex_unlock( &count_mutex );
    }
}

然而,这不是最佳的:互斥锁保持锁定的时间超过必要的时间。如果您不关心线程之间的任意输出交错,那么在调用count_mutex时不需要锁定printf(),只要您将新的count值的本地副本传递给printf()即可。您也可以在退出测试中使用该本地副本。

此外,只需要在functionCount2()改变count后测试信号条件。 pthread_mutex_signal()不必在持有互斥锁的情况下调用,所以我们也可以在printf()之后使用计数的本地副本:

void *functionCount1()
{
    for(;;)
    {
        int my_count;

        pthread_mutex_lock( &count_mutex );
        while( count >= COUNT_HALT1 && count <= COUNT_HALT2 )
        {
            pthread_cond_wait( &condition_cond, &count_mutex );
        }

        count++;
        my_count = count;
        pthread_mutex_unlock( &count_mutex );

        printf("Counter value functionCount1: %d\n", my_count);

        if (my_count >= COUNT_DONE)
            return(NULL);
    }
}

void *functionCount2()
{
    for(;;)
    {
        int my_count;

        pthread_mutex_lock( &count_mutex );
        count++;
        my_count = count;
        pthread_mutex_unlock( &count_mutex );

        printf("Counter value functionCount2: %d\n", my_count);

        if ( my_count < COUNT_HALT1 || my_count > COUNT_HALT2 )
        {
            pthread_cond_signal( &condition_cond );
        }

        if (my_count >= COUNT_DONE)
            return(NULL);
    }
}

如上所述,您可能不会再看到以严格计数顺序排序的printf()输出,因为我们不再强制printf()以增量的形式原子地发生。

© www.soinside.com 2019 - 2024. All rights reserved.