博客
关于我
【Linux】多线程---互斥锁,同步
阅读量:502 次
发布时间:2019-03-07

本文共 3466 字,大约阅读时间需要 11 分钟。

互斥与互斥锁的实现原理

在并发编程中,互斥是保证多个线程无法同时访问同一资源的关键机制。为了实现互斥,通常会采用互斥锁(Mutual Exclusion Lock, Mutual Lock)。

互斥锁本质上是一个同步机制,它通过确保只有一个线程能够在任何时刻占据整个资源的代码区域(临界区),从而防止资源竞争和数据 races。

互斥锁的内部实现

在互斥锁的内部,维护了一个计数器(Counter),也称为互斥量(Mutual Exclusion Degree)。这个计数器只有两种可能的值:

  • 0:表示当前没有线程持有互斥锁。
  • 1:表示当前有一个线程正在持有互斥锁。
  • 当线程试图获取互斥锁时,会检查计数器的值:

    • 如果计数器值为 0,表示没有其他线程持有互斥锁,此时当前线程可以获取互斥锁。
    • 如果计数器值为 1,则表示当前已经有一个线程持有互斥锁,此时当前线程无法获取互斥锁,必须等待。

    获取互斥锁属于原子操作,确保在修改计数器值时不会有其他线程观察到部分更新状态,避免可能的竞态条件。

    获取互斥锁的过程

    互斥锁的加锁过程分为三个步骤:

  • 检查寄存器中的值

    • 如果寄存器值为 1,表示当前可以加锁,直接将计数器值设置为 0,并通知线程开始访问临界区。
    • 如果寄存器值为 0,这表示有其他线程正在持有互斥锁,当前线程需要等待。
  • 交换寄存器与计数器的值

    这一步实际上是一个原子操作,确保在加锁时只允许一个线程成功获取互斥锁。

  • 返回加锁结果

    • 如果成功加锁(寄存器值为 1),返回成功标识,并修改计数器值。
    • 如果失败(寄存器值为 0),进入等待状态,直到计数器值变为 1
  • 互斥锁的加锁过程必须保证原子性,这意味着在获取互斥锁时,只能有一个线程能够成功加锁,避免数据竞争。

    互斥锁的接口与操作

    互斥锁的接口定义在 POSIX 标准中,主要包括以下几个函数:

    互斥锁的初始化

    // 静态互斥锁pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

    互斥锁的加锁函数

    // 阻塞加锁int pthread_mutex_lock(pthread_mutex_t *mutex);// 非阻塞加锁int pthread_mutex_trylock(pthread_mutex_t *mutex);// 带有超时时间的加锁int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);

    解锁函数

    int pthread_mutex_unlock(pthread_mutex_t *mutex);

    销毁互斥锁

    // 销毁互斥锁void pthread_mutex_destroy(pthread_mutex_t *mutex);

    条件变量与同步

    在多线程应用中,除了互斥锁外,还需要确保某些资源只有在特定条件满足时才能被访问。这个时候,条件变量(Condition Variable)发挥着重要作用。

    条件变量的作用

    条件变量通过维护一个等待队列和相关的唤醒操作,实现线程间的协调。即,当某个线程发现在某个条件不满足时(例如资源未准备就绪),它会将自身放入等待队列,等待其他线程的通知。当目标条件满足时,等待队列中的线程会被唤醒,继续执行任务。

    条件变量的接口

    条件变量的主要操作包括:

    • 初始化

      int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
    • 等待接口

      int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    • 唤醒接口

      int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);
    • 释放条件变量

      void pthread_cond_destroy(pthread_cond_t *cond);

    代码示例

    以下是一个使用互斥锁和条件变量的典型示例,它模拟了生产者和消费者对资源的互斥访问:

    #include 
    #include
    #include
    #define THREADCOUNT 2int g_bowl = 0;pthread_mutex_t g_mut;pthread_cond_t g_cond;void* MakeStart(void *arg) { while (1) { pthread_mutex_lock(&g_mut); while (g_bowl > 0) { pthread_cond_wait(&g_cond, &g_mut); } g_bowl++; printf("I am Make %p, g_bowl: %d\n", pthread_self(), g_bowl); pthread_mutex_unlock(&g_mut); pthread_cond_signal(&g_cond); } return NULL;}void* EatStart(void *arg) { while (1) { pthread_mutex_lock(&g_mut); while (g_bowl <= 0) { pthread_cond_wait(&g_cond, &g_mut); } g_bowl--; printf("I am Eat %p, g_bowl: %d\n", pthread_self(), g_bowl); pthread_mutex_unlock(&g_mut); pthread_cond_signal(&g_cond); } return NULL;}int main() { pthread_mutex_init(&g_mut, NULL); pthread_cond_init(&g_cond, NULL); pthread_t prod[THREADCOUNT], cons[THREADCOUNT]; for (int i = 0; i < THREADCOUNT; i++) { if (pthread_create(&prod[i], NULL, MakeStart, NULL) < 0) { perror("prod create error!\n"); return -1; } if (pthread_create(&cons[i], NULL, EatStart, NULL) < 0) { perror("cons create error!\n"); return -1; } } for (int i = 0; i < THREADCOUNT; i++) { pthread_join(prod[i], NULL); pthread_join(cons[i], NULL); } pthread_mutex_destroy(&g_mut); pthread_cond_destroy(&g_cond); return 0;}

    总结

    互斥锁是实现多线程安全的核心机制,它通过确保唯一的线程能够访问临界资源来避免竞态条件和数据 races。而条件变量则为线程间的协调提供了更高级的控制手段,特别是在资源仅在特定条件下可用时,条件变量能够有效地管理等待队列和唤醒过程。

    在实际应用中,互斥锁和条件变量通常结合使用,以实现更复杂的同步需求。通过合理使用这些机制,可以有效地构建高效且安全的多线程应用程序。

    转载地址:http://enacz.baihongyu.com/

    你可能感兴趣的文章
    android进程管理策略,Android进程保活
    查看>>
    arduino蓝牙通讯代码_arduino 联接蓝牙模块
    查看>>
    asp.mvc 4项目发布文件目录结构_如何用SpringBoot(2.3.3版本)快速搭建一个项目?文末有小彩蛋...
    查看>>
    aspen串联反应怎么输入_如何进步提升串联谐振试验装置的稳定性
    查看>>
    aspose html转pdf_Java实现Word/Pdf/TXT转html
    查看>>
    a推b等价于非a或b_AB胶/蜜月胶常见问题的原因分析及解决方法
    查看>>
    bat 命令返回结果_【批处理】带你入门命令行
    查看>>
    c++ string取子串_Integer与String的设计哲学
    查看>>
    c++ 数组批量赋值_数组之间不能赋值?穿个马甲吧!
    查看>>
    cad模糊查询符号_mysql 正则模式和like模糊查询
    查看>>
    continue可以用if判断里面吗_谁能说说if()else()里的continue是干嘛的?
    查看>>
    ctrl c 和 ctrl v 不能用了_神奇操作,原来CTRL键还能这么用
    查看>>
    cytoscape安装java_Cytoscape史上最全攻略
    查看>>
    c语言程序设计年历显示,C语言程序设计报告《万年历》.doc
    查看>>
    C语言程序设计梁海英答案,1.5 习题
    查看>>
    c语言编写单片机中断,C语言AVR单片机中断程序写法
    查看>>
    #pragma region、{}
    查看>>
    ddr2的上电顺序_S5PV210 DDR2初始化 28个步骤总结
    查看>>
    deque stack java_「集合系列」- 初探 java 集合框架图
    查看>>
    easyexcel 导出 代码翻译converter_【starter推荐】简单高效Excel 导出工具
    查看>>