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

linux----------------------线程同步与互斥(上)

1.线程互斥

1-1 进程线程间的互斥相关背景概念

临界资源:多线程执行流共享的资源就叫做临界资源

临界区:每个线程内部访问临界资源的代码就叫做临界区

互斥:任何时刻,互斥保证只有一个执行进入临界区,对临界资源起到保护作用

原子性:不会被任何调度打断的操作,只有两种状态一个是完成一个是未完成

1-2互斥量mutex

大部分情况线程使用的数据都是局部变量,变量的地址存放在线程栈空间里面,这种情况,变量归属单个线程,其他线程无法获得该变量。有的时候多个线程需要对同一个变量进行操作找个变量称为共享变量,多个线程并发的操作共享变量会带来一些问题

// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;void *route(void *arg)
{
char *id = (char*)arg;
while ( 1 ) {
if ( ticket > 0 ) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;} 
else {break;}
}
}
int main( void )
{pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, "thread 1");pthread_create(&t2, NULL, route, "thread 2");pthread_create(&t3, NULL, route, "thread 3");pthread_create(&t4, NULL, route, "thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);
}
⼀次执⾏结果:
thread 4 sells ticket:100
...
thread 4 sells ticket:1
thread 2 sells ticket:0
thread 1 sells ticket:-1
thread 3 sells ticket:-2

为什么只有100张票到0了的时候还有票能售出去呢?

这就是线程并发执行处理共享数据带来的问题,在我处理最后一张票的时候其他线程也进来了他们看到的都是1此时他们还不知道这个票已经被其他线程给执行完了。如何解决这个问题呢?

代码必须要有互斥行为:当代码进入临界区执行时不允许其他线程进入临界区

如果多个线程同时要求执⾏临界区的代码,并且临界区没有线程在执⾏,那么只能允许⼀个线程
进⼊该临界区。
如果线程不在临界区中执⾏,那么该线程不能阻⽌其他线程进⼊临界区。
要做到这三点只需要在临界区资源上一把锁。linux上提供的这把锁就叫做互斥量

互斥量的接口

初始化互斥量有两种第

1 静态分配

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
2 动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const
pthread_mutexattr_t *restrict attr);
参数:
mutex:要初始化的互斥量
attrNULL
销毁互斥量
销毁互斥量需要注意
使⽤ PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
不要销毁⼀个已经加锁的互斥量
已经销毁的互斥量,要确保后⾯不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

互斥量加锁和解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号

调用 pthread_lock是,可能会遇到以下情况

互斥量处于未上锁状态,该函数会讲把互斥量锁定返回成功

如何发起函数调用时,其他线程已锁定互斥量,没有竞争到互斥量资源,此时pthread_lock调用会陷入阻塞,直到互斥量解锁

修该上面的售票代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
int ticket = 100;
pthread_mutex_t mutex;
void *route(void *arg)
{
char *id = (char*)arg;
while ( 1 ) {
pthread_mutex_lock(&mutex);if ( ticket > 0 ) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread_mutex_unlock(&mutex);
// sched_yield(); 放弃CPU
} 
else {
pthread_mutex_unlock(&mutex);
break;
}}}int main( void )
{
pthread_t t1, t2, t3, t4;
pthread_mutex_init(&mutex, NULL);
pthread_create(&t1, NULL, route, "thread 1");
pthread_create(&t2, NULL, route, "thread 2");
pthread_create(&t3, NULL, route, "thread 3");
pthread_create(&t4, NULL, route, "thread 4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
pthread_mutex_destroy(&mutex);
}

1-3 互斥量的封装

test_4_22_httpserve/Mutex.h · liu xi peng/linux---ubuntu系统 - 码云 - 开源中国

大家有兴趣的可以点进去看一下

2.线程同步

2-1 条件变量

当一个线程互斥地访问某个变量时,他可能发现在其他线程状态改变之前他什么也做不了

例如⼀个线程访问队列时,发现队列为空,它只能等待,只到其它线程将⼀个节点添加到队列

中。这种情况就需要⽤到条件变量。

2-2 同步概念与竞态条件

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从⽽有效避免
饥饿问题,叫做同步
竞态条件:因为时序问题,⽽导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也
不难理解

2-3 条件变量函数

初始化

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t
*restrict attr);
参数:
cond:要初始化的条件变量
attrNULL
销毁
1 int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满⾜
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict
mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后⾯详细解释
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *active( void *arg )
{
std::string name = static_cast<const char*>(arg);
while (true){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
std::cout << name << " 活动..." << std::endl;
pthread_mutex_unlock(&mutex);
}
}
int main( void )
{
pthread_t t1, t2;
pthread_create(&t1, NULL, active, (void*)"thread-1");
pthread_create(&t2, NULL, active, (void*)"thread-2");
sleep(3); // 可有可⽆,这⾥确保两个线程已经在运⾏
while(true)
{
// 对⽐测试
// pthread_cond_signal(&cond); // 唤醒⼀个线程
pthread_cond_broadcast(&cond); // 唤醒所有线程
sleep(1);
}pthread_join(t1, NULL);
pthread_join(t2, NULL);
}

运行结果:

thread-1 活动...
thread-2 活动...
thread-1 活动...
thread-1 活动...
thread-2 活动...
2-4 ⽣产者消费者模型
321原则(便于记忆)
产消费模型是一种多线程并发的生产模型他有三种关系分别是消费者对消费者,生产者对生产者,消费着对生产者,分别是互斥,互斥,互斥和同步关系
由线程来承担消费者和生产者,线程可有多个,通常有一个特定的数据结构提供的缓冲区这个缓存区通常被成为交易场所
http://www.xdnf.cn/news/1107397.html

相关文章:

  • linux_线程概念
  • 基于开源AI智能名片链动2+1模式S2B2C商城小程序的营销直播质量提升策略研究
  • Vue框架之钩子函数详解
  • 2025年亚太杯(中文赛项)数学建模B题【疾病的预测与大数据分析】原创论文分享
  • [爬虫实战] 多进程/多线程/协程-异步爬取豆瓣Top250
  • QML与C++相互调用函数并获得返回值
  • PID控制算法理论学习基础——单级PID控制
  • 多 Agent 强化学习实践指南(一):CTDE PPO 在合作捕食者-猎物游戏中的应用详解
  • GitHub 操作指南:项目协作与自动化工作流实践
  • 【小沐杂货铺】基于Three.JS绘制汽车展示Car(WebGL、vue、react、autoshow、提供全部源代码)
  • 【Elasticsearch】function_score与rescore
  • html-初级标签
  • 【离线数仓项目】——数据模型开发实战
  • S7-200 SMART PLC:硬件、原理及接线特点全解析
  • 别再怕 JSON!5分钟带你轻松搞懂这个程序员的好帮手
  • C#调用Matlab生成的DLL
  • C++ Map 和 Set 详解:从原理到实战应用
  • win10安装Rust Webassembly工具链(wasm-pack)报错。
  • 细谈kotlin中缀表达式
  • RISC-V:开源芯浪潮下的技术突围与职业新赛道 (四) 产业应用全景扫描
  • Vim的magic模式
  • javaEE——synchronized关键字
  • Linux解决vim中文乱码问题
  • Spring AOP 是如何生效的(入口源码级解析)?
  • leetcode:HJ18 识别有效的IP地址和掩码并进行分类统计[华为机考][字符串]
  • 【Datawhale AI夏令营】mcp-server
  • [Python] Flask 多线程绘图时报错“main thread is not in main loop”的解决方案
  • 【unity实战】在Unity实现低耦合可复用的交互系统
  • 14. 请谈一下浏览器的强缓存和协商缓存
  • 基于大模型的鼻咽癌全周期预测及诊疗优化研究报告