C/Linux:线程间交替

C/Linux: Alternating between threads

所以我有这个代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <semaphore.h>

#define nr_threads 3
sem_t semaphores[nr_threads];
typedef struct {
    int id;
    char *word;
}th_struct;

void *thread_function(void *arg)
{
    th_struct *th_data = (th_struct *) arg; 

    sem_wait(&semaphores[th_data->id]);
        printf("[thread#%d] %s\n", th_data->id, th_data->word);
    sem_post(&semaphores[th_data->id + 1]);
    return NULL;
}

int main(int argc, char **argv)
{
    pthread_t tid[nr_threads];
    th_struct th_data[nr_threads];

    for(int i = 0; i < nr_threads; i++){
        if (sem_init(&semaphores[i], 0, 1) != 0){
            perror("Could not init semaphore");
            return -1;
        }
    }

    sem_post(&semaphores[0]);

    for(int i = 0; i < nr_threads; i++){
        th_data[i].id = i;
        th_data[i].word = argv[i + 1];
        pthread_create(&tid[i], NULL, thread_function, &th_data[i]);
    }

    for(int i = 0; i < nr_threads; i++){
        pthread_join(tid[i], NULL);
    }

    for(int i = 0; i < nr_threads; i++)
        sem_destroy(&semaphores[i]);

    return 0;
}

我从命令行给出3个词,例如"one two three",每个线程打印一个词,同步的,这样顺序总是正确的。

我是线程和信号量的新手,我的大脑目前习惯于sem_wait(sem)和sem_post(sem)之后,其中sem是一样的信号。我要问的是关于此代码为何工作以及如何工作的完整解释。为什么信号量初始化为 0 权限?为什么会有sem_post(first_semaphore)?我很困惑。

首先,该代码中存在错误...

完成工作后,每个线程无条件地调用下一个线程的信号量sem_post()。因此,第三个线程将尝试访问不存在的semaphores[3]

现在发生了什么(假设错误不存在)是这样的:

  • 创建并初始化了 3 个信号量,以便立即锁定它们
  • 创建了 3 个线程,每个线程都调用 sem_wait() 并阻塞(因为信号量被初始化为 0)
  • 一个线程完成它的工作后,它在下一个线程的信号量上调用 sem_post(),然后从 sem_wait()
  • returns

这是基本思路,但是要得到它运行,有人需要为第一个信号量调用sem_post()。所以这就是为什么在 main().

中有 sem_post(&semaphores[0])

注意:这是一个很长的评论,不是一个完整的答案。

我喜欢将信号量视为无信息令牌的阻塞队列。信号量的 count 是队列中的令牌数。

从这个角度来看,程序中的主线程创建了一个令牌(从无到有,因为令牌 什么都没有),并将令牌交给第一个工作线程通过调用 sem_post(&semaphores[0]);.

第一个 worker 在从其输入队列中取出令牌后能够完成其工作(即,当 sem_wait(&semaphores[th_data->id]); returns。完成工作后,它将令牌交给下一个主题:sem_post(&semaphores[th_data->id + 1]);