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

系统编程.10 同步和互斥

1.同步和互斥

一个公共资源同一时刻只能被一个进程或线程访问。

互斥:当多个进程或线程需要访问同一个公共资源的时候,不能同时使用,需要抢占。

同步:当多个进程或线程需要访问同一个公共资源的时候,不能同时使用,需要按照约定好的顺序使用。

1.1 互斥锁

互斥锁是一种简单加锁的方式来控制共享资源访问的方法。

互斥锁有两种状态:加锁(lock)和解锁(unlock)。

操作流程:

  • 在访问共享资源后临界区域前,对互斥锁进行加锁。 谁加上锁,就归谁使用
  • 在访问完成后释放互斥锁导上的锁。
  • 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

数据类型:pthread_mutex_t

1.2 API

1.2.1 pthread_mutex_init()

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutex_attr_t *restrict attr);

函数功能:

  • 初始化一个互斥锁

参数:

  • mutex:互斥锁地址。类型是 pthread_mutex_t 。
  • attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。

返回值:

  • 成功:0(申请成功的锁默认打开)
  • 失败:非0错误码

静态初始化互斥锁:

使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init(&mutex ,NULL) 来完成动态初始化,不同之处在于THREAD_MUTEX_INITIALIZER 宏不进行错误检查。

1.2.2 int pthread_mutex_destroy()

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

函数功能:

  • 销毁指定的互斥锁。

参数:

  • mutex:互斥锁地址。

返回值:

  • 成功:0
  • 失败:非 0 错误码

1.2.3 pthread_mutex_lock()

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

函数功能:

  • 对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。

参数:

  • mutex:互斥锁地址。

返回值:

  • 成功:0
  • 失败:非 0 错误码

1.2.4 pthread_mutex_unlock()

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

函数功能:

  • 对互斥锁解锁。

参数:

  • mutex:互斥锁地址。

返回值:

  • 成功:0
  • 失败:非 0 错误码
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>pthread_mutex_t mutex;void *print(void *argv);
void *func1(void *argv);
void *func2(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1,pt2;pthread_mutex_init(&mutex,NULL) ;pthread_create(&pt1,NULL,func1,"helloworld");pthread_create(&pt2,NULL,func2,"goodnightworld");pthread_join(pt1,NULL);pthread_join(pt2,NULL);pthread_mutex_destroy(&mutex);return 0;
}void *print(void *argv)
{int i=0;char *p=(char *)argv;while (*p!='\0'){printf("%c",p[i]);fflush(stdout);i++;sleep(1);}return 0;
}void *func1(void *argv)
{pthread_mutex_lock(&mutex);print(argv);pthread_mutex_unlock(&mutex);return NULL;
}
void *func2(void *argv)
{pthread_mutex_lock(&mutex);print(argv);pthread_mutex_unlock(&mutex);return NULL;
}

实现互斥只需要一把锁。

2.死锁

情况1:线程抢占资源后,由于某种原因突然消失,导致一直无法解锁。

情况2:两个线程同时执行,一个线程申请另一个正在使用的资源

情况3:两个线程同时申请另一个正在使用的资源

解决方法:

  • 进程开始前,一次性申请需要的所有资源
  • 允许进程只获得初期的资源就开始运行,运行完释放,再申请新的资源

3.条件变量

条件变量是用来等待而不是用来上锁的,条件变量本身不是锁!

条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。

通常条件变量和互斥锁同时使用。

条件变量的两个动作: 条件不满, 阻塞线程 当条件满足, 通知阻塞的线程开始工作。

3.1 API

3.1.1 初始化

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

函数功能:

  • 初始化一个条件变量

参数:

  • cond:指向要初始化的条件变量指针。

  • attr:条件变量属性,通常为默认值,传 NULL 即可

返回值:

  • 成功:0
  • 失败:非 0 错误码

静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

等价于:pthread_cond_init(&cond,NULL)

3.1.2 pthread_cond_destroy()

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

函数功能:

  • 销毁一个条件变量

参数:

  • cond:指向要初始化的条件变量指针。

返回值:

  • 成功:0
  • 失败:非 0 错误码

3.1.3 pthread_cond_wait()

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

函数功能:

  • 阻塞等待一个条件变量

a) 阻塞等待条件变量 cond(参 1)满足

b) 释放已掌握的互斥锁(解锁互斥量)相当于 pthread_mutex_unlock(&mutex);

a)b) 两步为一个原子操作(一起执行,没有中间态)。

c) 当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(&mutex);

参数:

  • cond:指向要初始化的条件变量指针
  • mutex:互斥锁

返回值:

  • 成功:0
  • 失败:非 0 错误号

3.1.4 唤醒所有

int pthread_cond_broadcast(pthread_cond_t *cond);

函数功能:

  • 唤醒全部阻塞在条件变量上的线程

参数:

  • cond:指向要初始化的条件变量指针

返回值:

  • 成功:0
  • 失败:非 0 错误号

4. 读写锁

特点:

  • 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。
  • 如果有其它线程写数据,则其它线程都不允许读、写操作

规则:

  • 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁。
  • 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。

4.1 API

4.1.1 pthread_rwlock_init()

#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

函数功能:

  • 用来初始化 rwlock 所指向的读写锁。

参数:

  • rwlock:指向要初始化的读写锁指针。
  • attr:读写锁的属性指针。如果 attr 为 NULL 则会使用默认的属性初始化读写锁,否则使用指定的 attr 初始化读写锁。

返回值:

  • 成功:0,读写锁的状态将成为已初始化和已解锁。
  • 失败:非 0 错误码。

静态初始化读写锁:

pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;

等价于pthread_rwlock_init(&rwlock,NULL) 来完成动态初始化,不同之处在于 PTHREAD_RWLOCK_INITIALIZER 宏不进行错误检查。

4.1.2 int pthread_rwlock_destroy()

#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

函数功能:

  • 用于销毁一个读写锁,并释放由 pthread_rwlock_init() 自动申请的资源 。

参数:

  • rwlock:读写锁指针。

返回值:

  • 成功:0。
  • 失败:非 0 错误码。

4.1.3 pthread_rwlock_rdlock()

 #include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

函数功能:

  • 如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁上多次执行读锁定。(如果有人在写,那么就不能申请读锁,所以会阻塞)

    线程可以成功调用 pthread_rwlock_rdlock() 函数 n 次,但是之后该线程必须调用 pthread_rwlock_unlock() 函数 n。次才能解除锁定。(保持次数一致)

参数:

  • rwlock:指向要初始化的读写锁指针。

返回值:

  • 成功:0。
  • 失败:非 0 错误码。

4.1.4 pthread_rwlock_wrlock()

 #include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

函数功能:

  • 如果调用线程未获取写锁,则它将阻塞直到它获取了该锁。(因为别的线程写的时候,不能申请写锁,所以会阻塞)

参数:

  • rwlock:指向要初始化的读写锁指针。

返回值:

  • 成功:0。
  • 失败:非 0 错误码。

4.1.5 pthread_rwlock_unlock()

#include <pthread.h>

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

函数功能:

  • 无论是读锁或写锁,都可以通过此函数解锁。

参数:

  • rwlock:读写锁指针。

返回值:

  • 成功:0
  • 失败:非 0 错误码


文章转载自:

http://YL1MDnXK.jwxnr.cn
http://SS8X5c2a.jwxnr.cn
http://X3UEAcC7.jwxnr.cn
http://Mkmb3yW5.jwxnr.cn
http://Iyh1Y0Sz.jwxnr.cn
http://hTv3EQTi.jwxnr.cn
http://t00XFoiV.jwxnr.cn
http://KObEnxm1.jwxnr.cn
http://cBuEC2k2.jwxnr.cn
http://YWYUlfz6.jwxnr.cn
http://f8ZRH6XS.jwxnr.cn
http://tplsU6k6.jwxnr.cn
http://IDvGB8hc.jwxnr.cn
http://MyXo7iOj.jwxnr.cn
http://BIFIiqBu.jwxnr.cn
http://E71LG4Nj.jwxnr.cn
http://yfBDhmUf.jwxnr.cn
http://rfNIcFEG.jwxnr.cn
http://wEYuhsCc.jwxnr.cn
http://321XCNNs.jwxnr.cn
http://unCuliS9.jwxnr.cn
http://vebElezi.jwxnr.cn
http://xFtuW9MV.jwxnr.cn
http://LmQzgdsP.jwxnr.cn
http://l1Tl08Jv.jwxnr.cn
http://oba7tUHU.jwxnr.cn
http://cbq0xaAN.jwxnr.cn
http://nIRyObsR.jwxnr.cn
http://BkRJ1A7N.jwxnr.cn
http://zpP69RpA.jwxnr.cn
http://www.dtcms.com/a/380871.html

相关文章:

  • Teable vs NocoDB 开源、在线协同 多维表格大PK
  • LINUX--编译器gcc/g++
  • 跨屏互联KuapingCMS建站系统发布更新 增加数据看板
  • 保证消息的可靠性
  • 从零开始搭建一个新的项目,需要配置哪些东西
  • 实施Ansible Playbook
  • 【每日算法】移除元素 LeetCode
  • 接口测试概念
  • 解析4口POE工控机的场景价值与核心优势
  • 【C++】STL 简介
  • 2025年渲染技术三大趋势:实时化、AI化与跨界融合
  • 固定资产系统如何降低企业管理成本?
  • Codeforces Round 1048 (Div. 2)与Codeforces Round 1049 (Div. 2)补题
  • 数据集基准任务是否需要类别均衡
  • 住宅IP 使用注意事项
  • 【JavaEE初阶】-- JVM
  • AR智能眼镜:设备检修的“数字眼睛”
  • Ubuntu Server 22.04.5系统安装教程
  • Python 循环导入问题
  • Redis延时双删详解
  • 关于商品数据采集的方式和注意事项
  • linux C 语言开发 (七) 文件 IO 和标准 IO
  • Java Servlet 完全解析:构建高效 Web 应用的关键技术
  • 【GIS】Cesium:快速加载地图
  • 【硬件-笔试面试题-92】硬件/电子工程师,笔试面试题(知识点:米勒效应,米勒平台)
  • 定点巡检、实时巡检详解和两者的区别对比
  • AI 编程工具选型速览(2025-09 版)
  • 2025年渗透测试面试题总结-66(题目+回答)
  • DOTA-Cys-Tyr-Leu-Ala-Ser-Arg-Val-His-Cys(一对二硫键)
  • ARPO: End-to-End Policy Optimization for GUI Agents with Experience Replay