linux多线程编程--信号量和条件变量 唤醒丢失事件

93 篇文章 1 订阅
19 篇文章 0 订阅

 关于linux下信号量和条件变量的使用,在很多地方都可以找到相关文章,信号量、条件变量、互斥锁都是线程同步原语,在平时多线程编程中只要知道一两种就可以轻松搞定,我也是这么认为的,但是今天发现,有时还是有区别的。

       在实现多线程编程中,其中有些东西是可以互相转换的,比如使用信号量可以实现条件变量,关于这三者的基本用法不在累述,我的博客中也有相关介绍,这里介绍条件变量丢失唤醒事件的事情。

    在线程未获得相应的互斥锁时调用 pthread_cond_signal 或pthread_cond_broadcast 函数可能会引起唤醒丢失问题。

唤醒丢失往往会在下面的情况下发生:

1、一个线程调用 pthread_cond_signal 或 pthread_cond_broadcast 函数;

2、另一个线程正处在测试条件变量和调用 pthread_cond_wait 函数之间;

3、没有线程正在处在阻塞等待的状态下。
下面是使用函数pthread_cond_wait()和函数pthread_cond_signal()的一个简单的例子:

pthread_mutex_t count_lock;

pthread_cond_t count_nonzero;

unsigned count;

decrement_count () {

pthread_mutex_lock (&count_lock);

while(count==0)

pthread_cond_wait( &count_nonzero, &count_lock);

count=count -1;

pthread_mutex_unlock (&count_lock);

}

increment_count(){

pthread_mutex_lock(&count_lock);

if(count==0)

pthread_cond_signal(&count_nonzero);

count=count+1;

pthread_mutex_unlock(&count_lock);

}
count  值为  0  时,  decrement  函数在  pthread_cond_wait  处被阻塞,并打开互斥锁 count_lock  。此时,当调用到函数  increment_count  时,  pthread_cond_signal  ()函数改变条件变量,告知  decrement_count  ()停止阻塞。读者可以试着让两个线程分别运行这两个函数,看看会出现什么样的结果。

函数 pthread_cond_broadcast ( pthread_cond_t *cond )用来唤醒所有被阻塞在条件变量 cond 上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。

在自己的测试过程也会发现丢失事件,即使按照非常严格的条件变量的使用方法。但是使用信号量就不会出现这种情况,因为信号量在内核中有相应的队列记录了信号的存在,不会将信号丢失。参考下面的代码:
#include 
#include 
#include 
#include 
#include 

void *read_func(void* args);
void *write_func(void* args);
pthread_mutex_t mutex;
pthread_cond_t cond;
sem_t sem;
int signal =0;
int count_read=0,count_write = 0;
int main()
{
  pthread_t pid1,pid2,pid3;
  pthread_mutex_init(&mutex,NULL);
  pthread_cond_init(&cond,NULL);

  sem_init(&sem,0,0);
  int ret;

  ret=pthread_create(&pid2,NULL,write_func,NULL);
  if(ret!=0)
  {
    printf("can not create write_funx!\r\n");
  }
  usleep(1);
  ret=pthread_create(&pid1,NULL,read_func,NULL);
  if(ret!=0)
  {
    printf("can not create read_funx!\r\n");
  }
  pthread_join(pid1,NULL);
  pthread_join(pid2,NULL);
  return 0;
}
void *read_func(void* args)
{
  for(;;)
  {
#if 0
    pthread_mutex_lock(&mutex);
    count_read++;
    signal =1;
    pthread_cond_signal(&cond);	
    pthread_mutex_unlock(&mutex);
    usleep(2);
#endif
#if 1
    count_read++;
    sem_post(&sem);
#endif
    if(count_read >= 50000)
    {
      printf("count_read is %d,count_write is %d\r\n",count_read,count_write);
      sleep(1);
      exit(0);
    }
  }
}
void *write_func(void* args)
{
  for(;;)
  {
#if 0
    pthread_mutex_lock(&mutex);
    while(!signal)
    {

      pthread_cond_wait(&cond,&mutex);
    }
    count_write++;
    signal =0;

    pthread_mutex_unlock(&mutex);

#endif
#if 1
    sem_wait(&sem);
    count_write++;
#endif
    if(count_write != count_read)
    {
      printf("%d write %d\r\n",count_read,count_write);
      exit(0);
    }
  }
}
这里是为了测试唤醒的效果,所以线程同步的情况并不是很严格,在read_func线程中是将count_read加1,然后唤醒write_func线程,这个线程中是将count_write加1.如果每次唤醒都存在的话,那么在若干次唤醒后,count_write和count_read是相等的,如果在write_func线程中发现两者不相等就直接退出,条件编译分别对应使用条件变量和信号量的两种情况。在使用条件变量是,即使在pthread_cond_signal后sleep一段时间后,仍然会出现丢失事件,在每次的测试结果都是不一样的,有的不会出现唤醒丢失,有的在刚开始就会出现唤醒丢失的出现,有的在最后才出现唤醒丢失情况!      但是使用信号量就不会出现这种情况,其实可以使用信号量实现条件变量,只不过是在初始化的时候稍微做一点手脚而已!! 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值