信号量中的 P(&sem) 和 V(&sem) 如何影响代码?

How do P(&sem) and V(&sem) in semaphore affect the code?

我有这个主要代码执行 pthread_create,函数 "doit" 作为其参数之一。我有三个 doit 函数,其中每个函数的 P 和 V 位置不同,或者根本没有 P 和 V。我的问题是,每个输出会有何不同?更具体地说,每个 doit 函数的可能输出是什么?

目前我所知道的是 P(&sem) 会将 sem 值变为 0,而 V 会将值变为 1。但是,我很难解释它将如何影响代码。

到目前为止,我认为 doit 函数 #1 将导致 1个 2个 3个 因为 printf 和 i=i+1 由 P(&sem) 和 V(&sem) 很好地保护。

此外,在我看来,doit 函数 #2 的所有可能输出是 1, 2, 3/// 1, 3, 3/// 2, 2, 3/// 2, 3, 3/// 3, 3, 3。 如有错误请指正

但是,就可能的输出而言,我真的不确定多线程在 doit 函数 #3 时会发生什么。如果有任何帮助,我将不胜感激。

  sem_t sem;
  /* semaphore */


  int main(){
     int j;
     pthread_t tids[3];
     sem_init(&sem, 0,1);
     for (j=0; j<3; j++) {
        pthread_create(&tids[j], NULL, doit, NULL);
        }
     for (j=0; j<3; j++) {
        pthread_join(&tids[j], NULL);
        }
     return 0;
     }


  doit# 1.
  int i = 0;
  void *doit(void *arg){
     P(&sem);
     i = i + 1;
     printf("%d\n", i);
     V(&sem);
     }


  doit #2.
  int i = 0;
  void *doit(void *arg){
     P(&sem);
     i = i + 1;
     V(&sem);
     printf("%d\n", i);
     }


  doit #3.
  int i = 0;
  void *doit(void *arg){
     i = i + 1;
     printf("%d\n", i);
     }

第一个程序会打印

1
2
3

这是唯一一个对 i 的所有访问都受到 P/V.[=16 适当保护的程序=]

另外两个程序的行为未定义,因为一个线程中的写入可能与另一个线程中的读取同时发生, 并根据 C11 5.1.2.4p25:

  1. The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

C11 5.1.2.4p4

  1. Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location.

通常这类作业要求找出 2 和 3 之间的差异。它们是错误的。从C的角度来看,你已经打开了潘多拉魔盒,一切皆有可能。