首页 > 解决方案 > 使用 Semaphor、Mutex 和 PThread 进行多线程

问题描述

我和一个朋友目前正在为大学研究 c 语言的基本多线程示例。我们应该使用多线程缓冲区来解决生产者/消费者问题。我们有一个使用互斥量和条件变量的工作版本,但是尝试使用信号量和互斥量来解决这个问题遇到了三个主要问题。

问题 1:如果我们先启动消费者,他有时会随机使用无效字符并崩溃。

问题2:如果我们先启动producer,他有时直到consumer启动才产生任何chars,这就导致了问题1。

问题 3:我们的生产者不会填满整个缓冲区,每次插入缓冲区后消费者都在消费,无论有多少生产者。

根据我们给定的伪代码示例,至少问题 2&3 不应该存在。我真的很感谢任何答案,因为我目前无法找到错误。

消费者:

void *consumer_sem(void *args) {
    printf("cons started\n");
    char c;

    while (cons_running) {
        sem_wait(occupied);
        pthread_mutex_lock(&mutex);

        c = consume();

        pthread_mutex_unlock(&mutex);
        sem_post(free);

        printf("consumer consumed %c\n\n", c);
        sleep(2);
    }   
}

制片人:

void *producer1_sem(void *args) {    
    printf("prod1 started\n");    
    char c;    
    int index=0;

    while (prod1_running) {    
        c = lowercase[index];    
        index=next(index);    

        sem_wait(free);
        pthread_mutex_lock(&mutex);

        add(c);

        pthread_mutex_unlock(&mutex);    
        sem_post(occupied);

        printf("producer1 produced something!\n");    
        printf("%d elements in buffer\n\n",getElemsInBuffer());   
        sleep(3);    
    }    
}

主要的:

sem_t *occupied, *free;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main(void) {
    occupied=sem_open("/occupied", O_CREAT, 0644, 0);
    free=sem_open("/free", O_CREAT, 0644, BUFFER_SIZE);

    //some unrelated code called

    pthread_create(&thread_ids[0], NULL, producer1_sem, NULL);
    pthread_create(&thread_ids[1], NULL, producer2_sem, NULL);
    pthread_create(&thread_ids[2], NULL, consumer_cond, NULL);

}

标签: clinuxmultithreadingpthreadssemaphore

解决方案


函数sem_open创建全局(命名)信号量,它存在直到sem_unlink()被调用。

当您第二次(以及进一步)运行程序时,sem_open 重用已经存在的 semaphore,并且它的值不会被重置。您可以轻松检测到:

// With O_EXCL opening already existed semaphore will fail.
occupied=sem_open("/occupied", O_CREAT | O_EXCL, 0644, 0);
if(occupied == SEM_FAILED) {
    perror("Failed to create new semaphore");
    exit(1);
}

实际上,当信号量仅由单个进程(但许多线程)使用时,使用以下命令对其进行初始化就足够了sem_init

sem_t occupied;
//...
int main()
{
    sem_init(&occupied, 0 /* do not share between processes*/, 0 /* initial value*/);
    //...
}

或者,您可以在尝试创建新信号量之前销毁旧信号量:

// Destroy the semaphore if it exists.
sem_unlink("/occupied");
// Create the semaphore again.
occupied = sem_open("/occupied", O_CREAT | O_EXCL, 0644, 0);

推荐阅读