当前位置: 首页 > news >正文

【PTHREAD】线程互斥与同步之互斥锁

1 互斥属性类型

#define __SIZEOF_PTHREAD_MUTEXATTR_T 4
typedef union
{
    char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
    int __align;
} pthread_mutexattr_t;

2 初始化与销毁互斥属性

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);

初始化与销毁互斥属性对象。

  • pthread_mutexattr_init以默认值初始化互斥属性对象。使用一个未初始化的互斥属性对象,将导致不确定性的行为。如果输入NULL,将使用默认的互斥属性值。
  • pthread_mutexattr_destroy销毁指定的互斥属性对象。一个已销毁的对象可以被重新初始化。使用一个已销毁的互斥属性对象,将导致不确定的行为。该函数在LinuxThread环境中的实现不做任何事情。
  • 一个互斥属性对象被修改或销毁不影响使用该互斥属性对象创建的互斥量

3 互斥属性之锁类型

int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind);
  • 已弃用。使用pthread_mutexattr_settype & pthread_mutexattr_gettype
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind);

LinuxThread仅支持一个互斥属性mutex kind。该值有以下可选值。

  • PTHREAD_MUTEX_FAST_NP

    快速互斥。默认值

  • PTHREAD_MUTEX_RECURSIVE_NP

    递归互斥

  • PTHREAD_MUTEX_ERRORCHECK_NP带错误检查的互斥

  • 后缀NP指示旋转是不可移植的(non-portable

    mutex kind用于决定在企图加锁一个已经处于锁定状态的的互斥时将如何做。

    • 如果为PTHREAD_MUTEX_FAST_NP将挂起当前线程。
    • 如果为PTHREAD_MUTEX_ERRORCHECK_NP立即返回,并携带错误码EDEADLK
    • 如果为PTHREAD_MUTEX_RECURSIVE_NP立即返回,并携带成功运行的返回码

4 初始化与销毁互斥

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                       const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • pthread_mutex_init

    • 如果参数为NULL,使用默认属性进行初始化。正常初始化后,互斥的状态变成已初始化且未锁定状态。

    • 企图初始化一个已初始化的互斥对象,将导致不确定性行为。

    • PTHREAD_MUTEX_INITIALIZER亦能完成互斥的初始化,但其不做错误检查。

  • pthread_mutex_destroy

    • 互斥被销毁后变成一个非法值。一个已销毁的互斥对象可被重新初始化。引用一个已销毁的互斥将出现不可预期的行为。

    • 销毁一个未锁定且已初始化的互斥是安全的。企图销毁一个锁定状态或被引用(例如pthread_cond_timedwaitpthread_cond_wait)将导致不确定行为。

5 互斥之加锁解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 如果互斥锁已被另一个线程锁定,调用线程将阻塞直到互斥锁可用。

  • pthread_mutex_lockpthread_mutex_trylock除加锁一个已经处于锁定状态的互斥时,前者进入阻塞状态,直接返回外,二者没区别。

6 案例:互斥锁的使用

使用互斥修改上一节中出错的样例,使其达到预期的效果。

使用流程

  • 在每一个需要互斥的地方都需要先加锁,后解锁。
  • 加锁解锁必须成对使用,否则可能造成死锁
  • 最小代码块内进行加锁解锁,以提高程序运行效率
  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    pthread_mutex_t mutex;
    int g_num = 0;
    
    void *start_routine_01(void *ptr)
    {
        for (size_t i = 0; i < 10000; i++)
        {
            // 锁定最小单元
            pthread_mutex_lock(&mutex); // 锁定
            g_num++;
            pthread_mutex_unlock(&mutex); // 解锁
        }
        return (void *)NULL;
    }
    
    void *start_routine_02(void *ptr)
    {
        for (size_t i = 0; i < 10000; i++)
        {
            // 锁定最小单元
            pthread_mutex_lock(&mutex); // 锁定
            g_num++;
            pthread_mutex_unlock(&mutex); // 解锁
        }
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        pthread_mutexattr_t attr;      // 定义互斥属性
        pthread_mutexattr_init(&attr); // 初始化互斥属性
    
        pthread_mutex_init(&mutex, &attr); // 初始化互斥
        pthread_mutexattr_destroy(&attr);  // 销毁互斥属性
    
        pthread_t thread_id_01;
        pthread_t thread_id_02;
    
        pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
        pthread_create(&thread_id_01, NULL, start_routine_02, NULL);
    
        pthread_join(thread_id_01, NULL);
        pthread_join(thread_id_02, NULL);
    
        printf("计算结果为: %d\n", g_num);
    
        printf("主线程即将退出...\n");
    
        pthread_mutex_destroy(&mutex); // 销毁互斥
        exit(EXIT_SUCCESS);
    }
    
  • 结果

    计算结果为: 20000
    主线程即将退出…

相关文章:

  • vscode自动生成testbench
  • 【流放之路-装备制作篇】
  • BLE错误码全面解析连接失败原因错误码解析BLE Disconnect Reason
  • Tensorflow pb模型转tflite,并量化
  • 【PTHREAD】线程状态
  • 网易云音乐项目————项目准备
  • 计算机网络——应用层の选择题整理
  • LabVIEW通过网络传输数据
  • 【PTHREAD】线程属性
  • 如何做好项目管理?项目管理和团队协作是关键
  • 《嵌入式 – GD32开发实战指南》第20章 GD32的存储结构
  • Vue模块语法上(插值指令过滤器计算属性-监听属性)
  • 初识网络
  • Linux的OpenLava配置
  • MySQL如何记忆
  • JavaScript-如何实现克隆(clone)函数
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 2019.2.20 c++ 知识梳理
  • const let
  • passportjs 源码分析
  • SegmentFault 2015 Top Rank
  • SpringBoot 实战 (三) | 配置文件详解
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • vue数据传递--我有特殊的实现技巧
  • 从输入URL到页面加载发生了什么
  • 大整数乘法-表格法
  • 解决iview多表头动态更改列元素发生的错误
  • 十年未变!安全,谁之责?(下)
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 06-01 点餐小程序前台界面搭建
  • Hibernate主键生成策略及选择
  • 阿里云移动端播放器高级功能介绍
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​插件化DPI在商用WIFI中的价值
  • # 达梦数据库知识点
  • #define 用法
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (js)循环条件满足时终止循环
  • (LeetCode C++)盛最多水的容器
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (二)linux使用docker容器运行mysql
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)菜鸟学数据库(三)——存储过程
  • *1 计算机基础和操作系统基础及几大协议
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .Net IE10 _doPostBack 未定义
  • @Import注解详解
  • @Resource和@Autowired的区别
  • []利用定点式具实现:文件读取,完成不同进制之间的
  • [android] 看博客学习hashCode()和equals()