在 pthreads 中丢失唤醒 [英] Lost wakeups in pthreads

查看:61
本文介绍了在 pthreads 中丢失唤醒的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一些程序来尝试 pthread 条件等待.但问题是无法保证发出的信号会被捕获,从而导致线程失去唤醒.我该如何解决这个问题?

I've written a little to program to try out pthread conditional waits. But the problem is that there is no guarantee that a signal when sent out will be caught, thereby the thread losing the wakeup. How do I get around this?

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

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *thread_func1(void* arg){
  printf("thread1 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread1: signalling\n");
  pthread_cond_signal(&cond);
  printf("thread1: signalled\n");
  pthread_mutex_unlock(&mutex);
  printf("thread1: exiting\n");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..\n");
  pthread_cond_wait(&cond, &mutex);
  printf("thread2: signal received\n");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting\n");
  pthread_exit(0);
}

int main(int argc, char** argv){
  pthread_t thread1, thread2;

  pthread_create(&thread1, NULL, thread_func1, NULL);
  pthread_create(&thread2, NULL, thread_func2, NULL);

  pthread_join(thread1, 0);
  pthread_join(thread2, 0);

  return 0;
}

这是一个运行的输出:

thread1 started
thread1: signalling
thread2 started
thread2: waiting for signal..
thread1: signalled
thread1: exiting
// nothing happens now; where is the signal??

这是另一个(有效):

thread2 started
thread2: waiting for signal..
thread1 started
thread1: signalling
thread1: signalled
thread1: exiting
thread2: signal received
thread2: exiting
// program successfully exits

我现在不关心任何类型的临界区,所以我没有使用任何锁.

I'm not concerned about any kind of critical section for now, so I haven't used any locks.

我如何确保每次运行都能正常工作?

How do I ensure this thing works for each run?

我已经按照下面的 alk 回答编辑了代码.我已经添加了初始化程序和锁.我发布的原始代码是这里.

I have edited the code as per alk's answer below. I have added the initializers and locks. The original code I posted is here.

推荐答案

如您所见,线程 1 可能会在线程 2 调用 pthread_cond_wait() 之前发出条件变量的信号.条件变量不会记住"它已发出信号,因此唤醒将丢失.因此,您需要使用某种变量来确定线程 2 是否需要等待.

As you have noticed, thread 1 might signal the condition variable before thread 2 calls pthread_cond_wait(). The condition variable does not "remember" that it has been signaled, so the wakeup will be lost. Therefore, you need to use some kind of variable to determine whether thread 2 needs to wait.

int signalled = 0;

void *thread_func1(void* arg){
  printf("thread1 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread1: signalling\n");
  signalled = 1;
  pthread_cond_signal(&cond);
  printf("thread1: signalled\n");
  pthread_mutex_unlock(&mutex);
  printf("thread1: exiting\n");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..\n");
  if(!signalled) {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("thread2: signal received\n");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting\n");
  pthread_exit(0);
}

然而,这段代码仍然不正确.pthreads 规范指出虚假唤醒"可能发生在条件变量上.这意味着即使没有人调用 pthread_cond_signal()pthread_cond_broadcast()pthread_cond_wait() 也可能返回.因此,您需要在循环中检查标志,而不仅仅是一次:

However, this code is still not correct. The pthreads spec states that "spurious wakeups" may occur on condition variables. This means that pthread_cond_wait() might return even if nobody has called pthread_cond_signal() or pthread_cond_broadcast(). Therefore, you need to check the flag in a loop, rather than just once:

void *thread_func2(void* arg){
  printf("thread2 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..\n");
  while(!signalled) {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("thread2: signal received\n");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting\n");
  pthread_exit(0);
}

更新:结合条件变量和计数器功能的另一种方法是使用信号量.

Update: An alternate method to combine the function of a condition variable and a counter is to use a semaphore.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t sem;

void *thread_func1(void* arg){
  printf("thread1 started\n");
  printf("thread1: signalling\n");
  sem_post(&sem);
  printf("thread1: signalled\n");
  printf("thread1: exiting\n");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started\n");
  printf("thread2: waiting for signal..\n");
  sem_wait(&sem);
  printf("thread2: signal received\n");
  printf("thread2: exiting\n");
  pthread_exit(0);
}

int main(int argc, char** argv){
  pthread_t thread1, thread2;

  sem_init(&sem);

  pthread_create(&thread1, NULL, thread_func1, NULL);
  pthread_create(&thread2, NULL, thread_func2, NULL);

  pthread_join(thread1, 0);
  pthread_join(thread2, 0);

  sem_destroy(&sem);

  return 0;
}

这篇关于在 pthreads 中丢失唤醒的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆