Многопоточность с Semaphor, Mutex и PThread - PullRequest
0 голосов
/ 05 мая 2018

Я и мой друг в настоящее время работаем над основными примерами многопоточности для университета в c. Мы должны решить проблему производителя / потребителя с помощью многопоточного буфера. У нас есть рабочая версия, использующая мьютекс и условные переменные, но при попытке решить эту проблему с помощью семафоров и мьютекса возникли три основные проблемы.

Задача 1 : если мы сначала запустили потребителя, он иногда случайным образом потребляет неверный символ и вылетает.

Проблема 2: Если мы сначала запускаем производителя, он иногда не производит никаких символов до тех пор, пока не будет запущен потребитель, что приводит к проблеме 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);

}

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

Я установил и запустил (через файл 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[ :]*,\1.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
0 голосов
/ 06 мая 2018

Функция 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);
...