Как создать семафор в c? - PullRequest
       47

Как создать семафор в c?

4 голосов
/ 14 февраля 2020

Я пытаюсь воссоздать библиотеку "Blackbox". В моем классе CS, когда мы должны использовать семафоры (в нашем окончательном варианте на бумаге), мы получаем файл "sem.h". Есть 3 функции: одна для создания нового семафора с начальным числом токенов, одна для извлечения токена из семафора и одна для помещения токена в семафор. при 0 tokes любой поток, использующий блокирующую функцию, должен ждать токен.

для лучшего понимания я пытался воссоздать этот sem.h и sem. c на основе некоторых экзаменов, требующих реализации одного Funktions. так как все это должно быть сделано на бумаге, оно не компилируется, но я чувствую, что я близко

sem.h

typedef struct SEM SEM;
struct SEM *semCreate(int);
void P(struct SEM*); //tokens--
void V(struct SEM*); //tokens++

sem. c

#include "sem.h"
#include <pthread.h>
#include <errno.h>
typedef struct SEM{
    volatile int val; //number of tokens
    pthread_mutex_t m;
    pthread_cond_t c;
}SEM;


/*create new semaphore with #initVal tokens */
SEM *semCreate(int initVal){
    static SEM *sem ={
        .val=initVal
    };
    errno = 0;
    if((errno = pthread_mutex_init(&sem->m,NULL))!=0){
        return NULL;
    }
    if((errno = pthread_cond_init(&sem->c,NULL))!=0){
        return NULL;
    }
    return sem;
}
//take a token from the semaphore
void P(SEM *sem){
    if((errno = pthread_mutex_lock(&sem->m))!=0){
        return;
    }
    while(sem->val <=0){
        if((errno=pthread_cond_wait(&sem->c,&sem->m))!=0){
            pthread_mutex_unlock(&sem->m);
            return;
        }
        sem->val--;
        if(errno = pthread_mutex_unlock(&sem->m)!=0)return;
    }
}
//put a token into the semaphore
void V(SEM *sem){
    if((errno = pthread_mutex_lock(&sem->m))!=0){
        return;
    }

    sem-> val++;

    if((errno = pthread_cond_broadcast(&sem->c))!=0)return;
    if((errno=pthread_mutex_unlock(&sem->m)!=0)) return;
}


В том случае, если неясно, для чего это нужно: функции должны ограничивать количество потоков, которые могут одновременно обращаться к сегменту кода. Пример

//global
static SEM *sem = semCreate(1);
/.../
//critical segment in threadfunction
P(sem);
doReadAndWriteGlobalList();
V(sem);

, как только 1-й поток пропустит P () при любых последующих вызовах P не сможет передать его, пока V не будет вызван в тот же sem

при компиляции я получаю следующую ошибку:

sem.c: In function ‘semCreate’:
sem.c:14:3: error: field name not in record or union initializer
   .val=initVal
   ^
sem.c:14:3: note: (near initialization for ‘sem’)
sem.c:14:8: error: initialization makes pointer from integer without a cast [-Werror=int-conversion]
   .val=initVal
        ^~~~~~~
sem.c:14:8: note: (near initialization for ‘sem’)
sem.c:14:8: error: initializer element is not constant
sem.c:14:8: note: (near initialization for ‘sem’)
cc1: all warnings being treated as errors

Ответы [ 2 ]

3 голосов
/ 14 февраля 2020

Тебе не нужен статус c вар. Вы хотите создать новый объект (выделение памяти) каждый раз, когда вызывается semCreate. Таким образом,

static SEM *sem ={
    .val=initVal
};

должно быть

SEM *sem = malloc(sizeof(SEM));
sem->val = initVal;

Не забудьте освободить семафор, когда закончите с ним. Это включает в себя ошибки!

SEM *semCreate(int initVal){
    SEM *sem = malloc(sizeof(SEM));
    if (!sem)
        goto Error1;

    sem->val = initVal;

    errno = pthread_mutex_init(&sem->m, NULL);
    if (!errno)
       goto Error2;

    errno = pthread_cond_init(&sem->c, NULL);
    if (!errno)
       goto Error3;

    return sem;

Error3:
    pthread_mutex_destroy(&sem->m);
Error2:
    free(buf);
Error1:
    return NULL;
}

Помимо этого, ваш код имеет несколько проблем. Короче говоря, P совершенно неверно.

  • P может вызвать pthread_cond_signal с разблокированным мьютексом.
  • P может вернуться с заблокированным мьютексом.
  • P уменьшает значение, когда оно не положительно, когда предполагается уменьшить его, когда оно положительно.

И есть проблема, что и P, и V работают бессмысленно и даже вредная обработка ошибок. Пропустить разблокировку мьютекса, если трансляция не удалась? Да, не будем этого делать.

Давайте начнем с нуля с базового c решения, без учета безопасности и эффективности.

void V(SEM *sem) {
   ++sem->val;
}

void P(SEM *sem) {
   // Wait for the semaphore to have a positive value.
   while (sem->val < 1) {
      // This is where another thread could change sem->val.
   }

   --sem->val;
}

Теперь давайте сделаем это безопасный через взаимное исключение.

void V(SEM *sem) {
   pthread_mutex_lock(&sem->m);
   ++sem->val;
   pthread_mutex_unlock(&sem->m);
}

void P(SEM *sem) {
   pthread_mutex_lock(&sem->m);

   // Wait for the semaphore to have a positive value.
   while (sem->val < 1) {
      pthread_mutex_unlock(&sem->m);
      // This is where another thread could change sem->val.
      pthread_mutex_lock(&sem->m);
   }

   --sem->val;
   pthread_mutex_unlock(&sem->m);
}

Но это напряженное ожидание. Давайте использовать условие var для сна, пока семафор не изменится. (Помните, что cond_wait разблокирует предоставленный мьютекс при входе и блокирует его перед возвратом.)

void V(SEM *sem) {
   pthread_mutex_lock(&sem->m);

   ++sem->val;

   // Wake up a thread that's waiting, if any.
   if (sem->val > 0)
      pthread_cond_signal(&sem->c);

   pthread_mutex_unlock(&sem->m);
}

void P(SEM *sem) {
   pthread_mutex_lock(&sem->m);

   // Wait for the semaphore to have a positive value.
   while (sem->val < 1)
      pthread_cond_wait(&sem->c, &sem->m);

   --sem->val;

   // Wake up a thread that's waiting, if any.
   if (sem->val > 0)
      pthread_cond_signal(&sem->c);

   pthread_mutex_unlock(&sem->m);
}

Тада!


Примечания:

  • Нет смысла вызывать pthread_cond_broadcast, так как только один поток может изменять семафор одновременно. Используя как V, так и P вызов pthread_cond_signal, когда это уместно, мы избегаем бесполезного пробуждения потоков.
  • Мы можем не проверять, произошли ли сбой pthread_mutex_lock, pthread_mutex_unlock и pthread_cond_signal рабочий код, так как он не работает в результате ошибки кодирования.
1 голос
/ 16 февраля 2020

Ваш Procure неправильный. Внутри l oop вы отпускаете мьютекс; который делает недействительным как тест в верхней части l oop, так и ожидание cond.

Вы, вероятно, хотите что-то вроде:

   lock(&s->mutex);
   while (s->val <= 0) {
        wait(&s->cv, &s->mutex);
   }
   s->val--;
   unlock(&s->mutex);

Пробуждение из условной переменной является лишь указанием Вы должны перепроверить свое состояние; не утверждение, что ваше состояние наступило.

...