【问题标题】:C semaphore not locking as intendedC 信号量未按预期锁定
【发布时间】:2017-05-06 22:01:55
【问题描述】:

我一直在尝试通过这个简单的售票程序来学习信号量。唯一的问题是当前的信号量没有保护numTickets 和ticketsSold 的值。我发现这是因为有时售出的总票数加起来是 51。

信号量的实现是否正确?

#include <semaphore.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

int numTickets;
sem_t mySem;

void* sell_ticket(void *sellerNum) {
  int sell = (int) sellerNum;
  int ticketsSold = 0;

  while (numTickets > 0) {
    srand ( time(NULL) );
    int random_number = rand();
    for (int i = 1; i < random_number % 5 ; i++) {
      if (numTickets > 0) {
        sem_wait(&mySem);
        numTickets--;
        ticketsSold++;
        printf("Seller # %d  sold a ticket. Tickets left: %d\n", sell, numTickets);
        sem_post(&mySem);
      }
    }
  }
  printf("Seller #%d noticed all tickets sold! (I sold %d myself) \n", sell, ticketsSold);
}

int main() {
  numTickets = 50;
  int numSellers = 4;
  sem_init(&mySem, 0, 1);

  pthread_t sellerThread;

  for (int i = 0; i < numSellers; i++) {
    pthread_create(&sellerThread, NULL, sell_ticket, (void *)i);
  }
  for (int i = 0; i < numSellers; i++) {
    pthread_join(sellerThread, NULL);
  }
  printf("All tickets sold!\n");
  return 0;
}

【问题讨论】:

  • 我不是 pthreads 专家,但希望在另一个线程正在写入时读取 numTickets(在 if 和 while 中)是问题所在。您需要在受信号量保护的块中获取读-修改-写序列。
  • 我已经尝试了上述建议。可悲的是,经过各种测试,我得到了 51 的值。

标签: c locking semaphore


【解决方案1】:

我相信我可以看到您的代码存在两个问题:

1) 由于您启动了多个线程(每个卖家一个线程),您应该有一个 p_threads 数组,而不仅仅是一个。否则,pthread_join 不会加入所有创建的线程,而只会加入最后一个(因为每次创建新线程时,都会将句柄覆盖到之前创建的线程)

您的 main 应该类似于以下内容:

int main() {
  numTickets = 50;
  int numSellers = 4;
  sem_init(&mySem, 0, 1);

  pthread_t sellerThread[4];

  for (int i = 0; i < numSellers; i++) {
    pthread_create(&sellerThread[i], NULL, sell_ticket, (void *)i);
  }
  for (int i = 0; i < numSellers; i++) {
    pthread_join(sellerThread[i], NULL);
  }
  printf("All tickets sold!\n");
  return 0;
}

2) 此外,就像 Gene 已经正确评论的那样,您还需要使用信号量来读取票数(在 if 和 while 条件测试中)。否则,您仍然可能会遇到一个线程正在写入,另一个线程正在读取的情况。

但是请注意,在整个循环期间获取信号量将导致只有一个线程出售所有票。

因此,sell_ticket 函数应该如下所示:

void* sell_ticket(void *sellerNum) {
  int sell = (int) sellerNum;
  int ticketsSold = 0;

  while (true) {
    // Check the number of tickets left. If sold out break out of infinite loop
    sem_wait(&mySem);
    if (numTickets <= 0) {
      sem_post(&mySem);
      break;
    }
    sem_post(&mySem);

    srand ( time(NULL) );
    int random_number = rand();
    for (int i = 1; i < random_number % 5 ; i++) {
      // Wait for semaphore before reading numTickets value in if condtion
      sem_wait(&mySem);

      if (numTickets > 0) {
        numTickets--;
        ticketsSold++;
        printf("Seller # %d  sold a ticket. Tickets left: %d\n", sell, numTickets);
      }
       // Post after if
       sem_post(&mySem);
    }
  }
  printf("Seller #%d noticed all tickets sold! (I sold %d myself) \n", sell, ticketsSold);
}

【讨论】:

  • 我已经阅读了您的回答,现在我确实知道在 if 和 while 周围放置信号量是一个明智的选择。可悲的是,即使提供了代码。我偶尔会得到高于 50 的值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-02
相关资源
最近更新 更多