我正在尝试用信号量来实现生产者和消费者问题。我认为我的逻辑是正确的,但我的代码没有按预期工作。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#define BUFFER_SIZE 10
int arr[BUFFER_SIZE];
int position;
sem_t *mutex, *empty, *filled;
void* producer(void* args){
printf("Prod starting\n");
int a,b;
sem_getvalue(empty, &a);
sem_getvalue(mutex, &b);
printf("empty : %d mutex : %d\n", a, b);
do{
sem_wait(empty);
sem_wait(mutex);
int new = rand()%2000;
printf("Adding %d to buffer\n", new);
arr[position] = new;
position++;
sem_post(mutex);
sem_post(filled);
}while(1);
}
void* consumer(void* arg){printf("Consumer starting\n");
do{
sem_wait(filled);
sem_wait(mutex);
position--;
printf("Consuming %d from buffer", arr[position]);
arr[position] = 0;
sem_post(mutex);
sem_post(empty);
}while(1);
}
int main()
{
mutex = sem_open("mutex", O_CREAT, 0644, 1);
empty = sem_open("empty", O_CREAT, 0644, BUFFER_SIZE);
filled = sem_open("filled", O_CREAT, 0644, 0);
pthread_t prod, cons;
int a, b,c;
sem_getvalue(empty, &a);
sem_getvalue(filled, &b);
sem_getvalue(mutex, &c);
printf("empty:%d filled:%d mutex:%d\n", a,b,c);
//printf("Producer = %ld Consumer = %ld\n", prod, cons);
pthread_create(&prod,NULL,producer,NULL);
pthread_create(&cons,NULL,consumer,NULL);
pthread_join(prod,NULL);
pthread_join(cons,NULL);
sem_close(mutex);
sem_unlink("mutex");
sem_close(empty);
sem_unlink("empty");
sem_close(filled);
sem_unlink("filled");
return 0;
}
信号量未正确初始化,从以下输出可知:
empty:0 filled:0 mutex:0
Prod starting
empty : 0 mutex : 0
Consumer starting
我不明白为什么信号量没有正确初始化。从手册页:
sem_t *sem_open(const char *name, int oflag, mode_t 模式, unsigned int 值);
如果在 oflag 中指定了 O_CREAT,则需要两个附加参数 必须提供。 mode 参数指定权限 被放置在新的信号量上,该值 参数指定新信号量的初始值。
注意:我不能使用 sem_init,因为最终代码需要在 Macos 上完成。我只是用 sem_getvalue() 在 Linux 上重新创建这个来了解问题。
根据@pilcrow的建议,我将问题重新创建为:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t *mutex;
void* thread(void* arg){
printf("Entered thread\n");
sem_wait(mutex);
printf("Hello\n");
sem_post(mutex);
pthread_exit(NULL);
}
int main()
{
mutex = sem_open("mutex", O_CREAT, 0644, 1);
if(mutex == SEM_FAILED){
printf("Failed to create semaphore\n");
return 1;
}
pthread_t id;
pthread_create(&id,NULL,thread,NULL);
pthread_join(id,NULL);
return 0;
}
输出:
Entered thread
线程继续无限期地等待 mutex 信号量,而该信号量再次未正确初始化。
命名信号量由
形式的名称标识;也就是说,最多为/somename
的以空结尾的字符串 (即 251)个字符,由一个初始斜杠组成,后跟一个或多个字符,其中没有一个是斜杠。二 通过将相同的名称传递给NAME_MAX-4
,进程可以对同一个命名的信号量进行操作。sem_open(3)
函数创建一个新的命名信号量或打开一个现有的命名信号量。信号量被设置后 打开后,可以使用sem_open(3)
和sem_post(3)
进行操作。当进程使用完信号量后,它可以 使用sem_wait(3)
关闭信号量。当所有进程都使用完该信号量后,可以将其从 系统使用sem_close(3)
.sem_unlink(3)
多个进程可以打开同一个信号量,这意味着它必须存在于进程范围之外的某个地方
它需要手动取消链接,这表明即使每个打开它的进程都调用了
sem_close()
,它仍然存在(否则当引用计数变为零时它可能会被销毁)。
在 Linux 上的
strace
下运行测试过程表明信号量由文件 /dev/shm/sem/mutex
支持。其他平台可能有不同的支持位置,但必须支持相同的语义。
需要手动取消链接信号量的明显推论是,如果您不,那么每次您使用相同的名称打开它时都会得到相同的信号量,其中包含先前运行的最后一个值。
如果您确实只想在单个进程中使用信号量并且不能使用未命名的信号量(使用
sem_init()
),那么您应该确保名称是唯一的。从 PID 形成一个字符串将是一个好的开始,退出时取消链接也是如此。