【问题标题】:pthread and conditional variablespthread 和条件变量
【发布时间】:2019-02-23 08:41:48
【问题描述】:

我正在关注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 不应该算 3 吗?在functionCount1 的while 循环中,它在任何值包括 3 上调用wait(),这使我认为3 应该由functionCount2 计数,而不是functionCount1

为什么不是这样?

【问题讨论】:

  • 从多个线程对对象进行非同步、非只读、非原子访问的未定义行为。
  • @EOF 但是有互斥锁来确保我们同步 3 到 6 之间的值。作者甚至提到“唯一可以确保的是 functionCount2 将增加值之间的计数COUNT_HALT1 和 COUNT_HALT2。其他一切都是随机的。”
  • C11 标准草案 n1570:5.1.2.4 多线程执行和数据竞争 4 如果其中一个修改了内存位置,而另一个读取或修改了相同的内存位置,则两个表达式计算冲突. 25 如果一个程序在不同线程中包含两个相互冲突的操作,则该程序的执行包含数据竞争,其中至少一个不是原子的,并且两者都不会在另一个之前发生。任何此类数据竞争都会导致未定义的行为。 未定义。行为。

标签: concurrency pthreads condition-variable


【解决方案1】:

一个线程可以读取count,只持有condition_mutex(其中count针对COUNT_HALT1COUNT_HALT进行测试)或根本不持有互斥锁(其中count针对@987654327进行测试@) 而另一个线程可以修改count,而只保留count_mutex。这种不同步的访问会导致未定义的行为,正如@EOF 在问题 cmets 中指出的那样,因此代码根本不正确。

也就是说,即使我们只运行 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 );
    }
}

这不是最优的,但是:互斥锁被保持锁定的时间超过了必要的时间。如果您不关心线程之间的任意输出交错,那么您在调用printf() 时不需要锁定count_mutex,只要您获取新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() 随着增量原子发生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-13
    • 2019-04-05
    • 2021-08-24
    • 1970-01-01
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多