gpt4 book ai didi

c - 如何在c中创建信号量?

转载 作者:行者123 更新时间:2023-12-03 03:00:25 29 4
gpt4 key购买 nike

我正在尝试重新创建一个“Blackbox”库。在我的计算机科学类(class)中,当我们应该使用信号量时(在我们的纸质期末考试中),我们会得到一个“sem.h”文件。有 3 个函数,一个用于创建具有初始数量 token 的新信号量,一个用于从信号量中取出 token ,一个用于将 token 放入信号量中。在 0 处 token 任何使用阻塞函数的线程都必须等待 token 。

为了更好地理解,我一直在尝试根据一些要求实现单一功能的考试来重新创建这个 sem.h 和 sem.c。因为这一切都是在纸上完成的,所以它无法编译,但我觉得我很接近

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);

一旦第一个线程传递 P(),任何后续的 P 调用都将无法传递它,直到在同一个 sem 上调用 V

编译时出现以下错误:

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

最佳答案

你不需要静态变量。您希望每次调用 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 当数值为非正数时,它会减少数值,而当它应该在数值为正数时减少数值。

还有一个问题是,PV 都执行毫无意义甚至有害的错误处理。如果广播失败则跳过互斥体解锁?是的,我们不要这样做。

让我们从一个基本的解决方案开始,不考虑安全性或效率。

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);
}

但这是一个忙碌的等待。让我们使用条件变量来休眠,直到信号量发生变化。 (请记住,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 是没有意义的,因为一次只能有一个线程修改信号量。通过让 VP 在适当的时候调用 pthread_cond_signal,我们可以避免无缘无故地唤醒线程。
  • 我们可以省略检查 pthread_mutex_lockpthread_mutex_unlockpthread_cond_signal 在工作代码中是否失败,因为它们只会因编码错误而失败.

关于c - 如何在c中创建信号量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60224504/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com