如何在c中创建信号量?

2024-04-26

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

为了更好地理解,我一直在尝试根据一些要求实现单一功能的考试来重新创建这个 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当它是非正数时,它会减少该值,而当它是正数时,它应该减少它。

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

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

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

Tada!


Notes:

  • 打电话没有意义pthread_cond_broadcast因为一次只有一个线程可以修改信号量。通过同时拥有V and P call pthread_cond_signal在适当的时候,我们会避免无缘无故地唤醒线程。
  • 我们可以省略检查是否pthread_mutex_lock, pthread_mutex_unlock and pthread_cond_signal工作代码失败,因为这些仅因编码错误而失败。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在c中创建信号量? 的相关文章

  • 删除文件的最后 10 个字符

    我想删除文件的最后 10 个字符 说一个字符串 hello i am a c learner 是文件内的数据 我只是希望该文件是 hello i am a 文件的最后 10 个字符 即字符串 c learner 应在文件内消除 解决方案 将
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 根据属性的类型使用文本框或复选框

    如果我有这样的结构 public class Parent public string Name get set public List
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐