使用 Semaphor、Mutex 和 PThread 的多线程
Multithreading with Semaphor, Mutex and PThread
我和一个朋友目前正在为大学编写基本的多线程示例。我们应该用多线程缓冲区解决 producer/consumer 问题。我们有一个使用互斥量和条件变量的工作版本,但是试图解决这个 使用信号量和互斥量遇到了三个主要问题。
问题 1:如果我们先启动消费者,他有时会随机消费一个无效的字符并崩溃。
问题2:如果我们先启动producer,有时他会等到consumer启动后才会产生任何字符,这会导致问题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);
}
函数 sem_open 创建全局(named)信号量,该信号量在调用 sem_unlink()
之前一直存在。
当您运行您的程序第二次(以及更多)时,sem_open
重用已经存在的信号量,并且它的值不会被重置。您可以轻松检测到:
// 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);
我设置并 运行(通过 make
文件)发布的代码;
这是我使用的 makefile.mak
,我通过 make -f makefile.mak
运行
SHELL = /bin/sh
#
# macro of all *.c files
# (NOTE:
# (the following 'wildcard' will pick up ALL .c files
# (like FileHeader.c and FunctionHeader.c
# (which should not be part of the build
# (so be sure no unwanted .c files in directory
# (or change the extension
#
SRC := $(wildcard *.c)
#OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
#INC := $(SRC:.c=.h)
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CP := cp
MV := mv
#LDFLAGS := -L/usr/local/lib
DEBUG := -ggdb3
CCFLAGS := $(DEBUG) -Wall -Wextra -pedantic -Wconversion -std=gnu11
#CPPFLAGS += =MD
LIBS :=
.PSEUDO: all
all: gestore personA personB
#
# link the .o files into the executable
# using the linker flags
# -- explicit rule
#
getstore: getstore.o
#
# ======= $(name) Link Start =========
$(CC) $(DEBUG) -o $@ $^ $(LIBS)
# ======= $(name) Link Done ==========
#
personA: personA.o
#
# ======= $(name) Link Start =========
$(CC) $(DEBUG) -o $@ $^ $(LIBS)
# ======= $(name) Link Done ==========
#
personB: personB.o
#
# ======= $(name) Link Start =========
$(CC) $(DEBUG) -o $@ $^ $(LIBS)
# ======= $(name) Link Done ==========
#
#
#create dependancy files -- inference rule
# list makefile.mak as dependancy so changing makfile forces rebuild
#
%.d: %.c
#
# ========= START $< TO $@ =========
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
# ========= END $< TO $@ =========
#
# compile the .c file into .o files using the compiler flags
# -- inference rule
#
%.o: %.c %.d
#
# ========= START $< TO $@ =========
$(CC) $(CCFLAGS) -c $< -o $@ -I.
# ========= END $< TO $@ =========
#
.PHONY: clean
clean:
# ========== CLEANING UP ==========
rm -f *.o
rm -f *.d
rm -f getstore
rm -f personA
rm -f personB
# ========== DONE ==========
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependencies for that .c file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif
我和一个朋友目前正在为大学编写基本的多线程示例。我们应该用多线程缓冲区解决 producer/consumer 问题。我们有一个使用互斥量和条件变量的工作版本,但是试图解决这个 使用信号量和互斥量遇到了三个主要问题。
问题 1:如果我们先启动消费者,他有时会随机消费一个无效的字符并崩溃。
问题2:如果我们先启动producer,有时他会等到consumer启动后才会产生任何字符,这会导致问题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);
}
函数 sem_open 创建全局(named)信号量,该信号量在调用 sem_unlink()
之前一直存在。
当您运行您的程序第二次(以及更多)时,sem_open
重用已经存在的信号量,并且它的值不会被重置。您可以轻松检测到:
// 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);
我设置并 运行(通过 make
文件)发布的代码;
这是我使用的 makefile.mak
,我通过 make -f makefile.mak
SHELL = /bin/sh
#
# macro of all *.c files
# (NOTE:
# (the following 'wildcard' will pick up ALL .c files
# (like FileHeader.c and FunctionHeader.c
# (which should not be part of the build
# (so be sure no unwanted .c files in directory
# (or change the extension
#
SRC := $(wildcard *.c)
#OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
#INC := $(SRC:.c=.h)
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CP := cp
MV := mv
#LDFLAGS := -L/usr/local/lib
DEBUG := -ggdb3
CCFLAGS := $(DEBUG) -Wall -Wextra -pedantic -Wconversion -std=gnu11
#CPPFLAGS += =MD
LIBS :=
.PSEUDO: all
all: gestore personA personB
#
# link the .o files into the executable
# using the linker flags
# -- explicit rule
#
getstore: getstore.o
#
# ======= $(name) Link Start =========
$(CC) $(DEBUG) -o $@ $^ $(LIBS)
# ======= $(name) Link Done ==========
#
personA: personA.o
#
# ======= $(name) Link Start =========
$(CC) $(DEBUG) -o $@ $^ $(LIBS)
# ======= $(name) Link Done ==========
#
personB: personB.o
#
# ======= $(name) Link Start =========
$(CC) $(DEBUG) -o $@ $^ $(LIBS)
# ======= $(name) Link Done ==========
#
#
#create dependancy files -- inference rule
# list makefile.mak as dependancy so changing makfile forces rebuild
#
%.d: %.c
#
# ========= START $< TO $@ =========
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
# ========= END $< TO $@ =========
#
# compile the .c file into .o files using the compiler flags
# -- inference rule
#
%.o: %.c %.d
#
# ========= START $< TO $@ =========
$(CC) $(CCFLAGS) -c $< -o $@ -I.
# ========= END $< TO $@ =========
#
.PHONY: clean
clean:
# ========== CLEANING UP ==========
rm -f *.o
rm -f *.d
rm -f getstore
rm -f personA
rm -f personB
# ========== DONE ==========
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependencies for that .c file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif