Linux相关概念和易错知识点(30)(线程互斥、线程同步)

Verilog--CDC跨时钟域处理(快时钟域到慢时钟域) Verilog--CDC跨时钟域处理(快时钟域到慢时钟域)CDC问题单比特信号的跨时钟域问题从快时钟域到慢时钟域从慢时钟域到快时钟域多比特信号的跨时钟域问题异步FIFO握手协议 今天先写单比特信号从快时钟域到慢时钟域的方法,后续继续填坑 CDC问题 单比特信号的跨时钟域问题 从快时钟域到慢时钟域 设计思想   从慢时钟域到快时钟域通常使用打两拍的方法,这也适用于从快时钟域到慢时钟域时,但是如果当单比特信号持续时间小于快时钟域的一个周期时,需要用展宽信号再打两拍的方法。 设计相关代码 module Sync_ 阅读详情

目录

1.线程互斥

(1)临界资源和临界区

(2)互斥和原子性

①互斥

②原子性

(3)加锁和解锁(互斥锁)的原理

(4)pthread_mutex系列函数和变量

①lock、unlock

②pthread_mutex_t

(5)加锁常见错误

(6)对互斥锁的认识

2.线程同步

(1)线程互斥引发的问题

(2)pthread_cond系列函数和变量

①pthread_cond_t

②wait、signal


1.线程互斥

(1)临界资源和临界区

临界资源:多执行流中需要被保护的共享的资源。如某一个函数可被所有线程访问,但是在同一时刻所有线程都进入该函数会导致这个函数执行异常(如打印混乱),那么这个函数就是不可重入函数,也就是需要被保护的共享资源,即临界资源。对于线程来说,所有函数代码都是共享的,因此对多线程来说临界资源就是要防止多个线程同时访问的资源。

临界区:每个线程内部访问临界资源的代码。临界区是代码,保护临界资源的手段就是保护临界区,只要访问临界资源的代码受到保护,那么对应的临界资源就会受到保护。简而言之,保护临界区是手段,保护临界资源才是目的。

(2)互斥和原子性

①互斥

先看一下下面的代码,最后m的结果应该是多少呢?


#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

int m = 10;

void *fun(void *p)
{
    while (m > 0)
    {
        sleep(1);
        m--;
    }
    return nullptr;
}

int main()
{
    pthread_t pid_1, pid_2, pid_3, pid_4, pid_5;
    pthread_create(&pid_1, nullptr, fun, (void *)nullptr);
    pthread_create(&pid_2, nullptr, fun, (void *)nullptr);
    pthread_create(&pid_3, nullptr, fun, (void *)nullptr);
    pthread_create(&pid_4, nullptr, fun, (void *)nullptr);
    pthread_create(&pid_5, nullptr, fun, (void *)nullptr);

    pthread_join(pid_1, nullptr);
    pthread_join(pid_2, nullptr);
    pthread_join(pid_3, nullptr);
    pthread_join(pid_4, nullptr);
    pthread_join(pid_5, nullptr);

    printf("m = %d\n", m);

    return 0;
}

结果是

事实上,每次结果都可能不相同,在以上实例中,m可以被所有线程访问,即共享资源,访问修改这个共享资源的代码就是fun函数。如果所有线程同时访问这块代码,就有可能出现错误。

例如在m = 1时,pid_1线程执行到while (m > 0)这个语句时,会先将m的值从内存读到寄存器中,当读完之后,此时该线程时间片到了被切走后,该线程的寄存器的上下文被保存了下来。切到另一个线程之后,如果它执行到while (m > 0)这个语句时,它也会将m的值从内存读到寄存器中,其余线程也如此,这样就会导致判断语句将多个线程放进去了。每个线程的状态都不一样,当pid_1将m--完成之后,可能pid_2才会执行到这里,这个时候m又被--,同理,pid_3和pid_4都参与m--,导致m最终为负数。

因此这个m其实是需要被保护起来的,即临界资源,同样,fun函数中访问临界资源的代码(临界区)也需要被保护。我们通过保护临界区来保护临界资源。

这就叫线程互斥,即任意时刻只能有一个执行流进入临界区,访问临界资源。

②原子性

执行判断语句,CPU分为3步执行,即从内存获取数据到寄存器、分析、执行指令三步。

自减语句也分为3步,即从内存获取数据到寄存器、自减、写回内存三步。

以上两个语句均会被拆分为三个指令执行,并且这三个指令都同等重要,当任意一个指令被执行后就立马切走线程,就有可能导致出现错误。也就是说,执行判断语句和自减不是原子性的。

但是,导致上述代码的错误并不是单纯是因为这两个语句不是原子的,而是while循环的代码这个整体不是原子的。原子指的就是要么完成,要么还没完成,它不会被任何调度机制打断。从while循环的第一个判断语句的第一条指令开始,就视为这个代码块开始执行了,如果这个while循环的代码是原子的,就只有执行完和还没执行完两种状态,那么当第一个线程开始执行第一条指令后,就算存在时间片的轮转,其它线程也无法进入while的执行,因为第一个线程还没执行完。

从另一个角度来理解,一行指令肯定是原子的,因为无论时间片是怎样的,这条指令要么还没执行,要么就必须执行完,不存在执行了一半就被切走了。对于这条指令来说,同一时间内一定只有一个线程在执行它,只要当前线程开始执行它,那么就必须等这个线程执行完这个指令后,其它线程才有机会执行这个指令。

现在我们拓展一条指令为一个代码块,就是我前面所讲的例子。

总结:导致临界资源出现混乱的原因是临界区同时被多个线程访问。其根本原因是while循环代码块整体不是原子的,能同时放进来多个线程。如果while循环整体是原子的,那么一次性就只能允许一个线程进入,也就不会导致临界资源的错误了。

下面我们就要详细讲讲如何将一整个代码块变为原子的。

(3)加锁和解锁(互斥锁)的原理

首先当锁被创建之后,内存中就会新建一个变量mutex,这个变量默认为1。CPU内部有一个寄存器eax,在执行lock指令前默认先设为0,当调用lock指令时,eax和mutex里面的数据会进行交换(寄存器和内存里面的值进行交换),这个时候eax里面就为1,mutex里面的值为0。mutex不会主动修改自己的值,因此它会一直是0。如果执行lock指令之后eax为1,那么就让这个线程执行被加锁的代码。

在该线程执行代码期间,如果有其它线程来申请锁同样需要上面的步骤,eax设置为0,和mutex交换,判断eax是否为1。关键之处在于,当第二个线程交换mutex和eax时,eax本身是0不必多说,但mutex里面也是0,因为第一个线程已经把mutex里面的1交换走了!当线程切换时,第一个线程的eax的1被当作硬件上下文保存起来了,只留下了一个值为0的mutex在内存中。也就是说不管线程怎么切换,只要第一个线程不把mutex的1交换回去,其他线程无论怎么申请,都无法得到那个1。如果eax不是1,那么线程会被阻塞在加锁的函数里,隔一段时间后再次设eax为0,和mutex交换,判断eax。

因此加锁的核心就是那个交换寄存器和内存变量值的指令,只要谁先把内存中mutex的1拿走,谁就拥有临界区的访问权,而其它线程都必须等在原地。只有解锁时,线程把那个1还给mutex之后,其它线程才有机会进入临界区。通过这种方式,就实现了对整个代码块加锁,使其在线程看来是原子的。

形象的理解:mutex就是一间屋子外面墙上挂着的钥匙,并且这钥匙一次性只能给一个人用,谁拿到钥匙谁就可以用这个房间,并且其他人永远进不来,只能在外面等着。使用屋子的人可以中途上厕所、吃饭等,只要不重新把钥匙挂回墙上,这个屋子始终是他的。对其他人来说,拿着钥匙的人怎么用屋子这件事就是原子的,要么没用完,要么用完了。当使用这屋子的人将钥匙重新挂回墙上,其它人才能来抢钥匙,抢夺占有权。

为了实现互斥锁的操作,大多数体系结构都提供了swap和exchange指令,这个指令可以让寄存器和内存单元交换数据。

(4)pthread_mutex系列函数和变量

①lock、unlock

pthread_mutex_lock( pthread_mutex_t * mutex )加锁,pthread_mutex_unlock( pthread_mutex_t * mutex )解锁。

pthread_mutex_lock就是执行上述eax设置为0,交换eax和mutex,判断eax是否为1的指令集合,那个1就是锁,凡是没有申请到锁的线程会被阻塞在函数中,直到1被还回来了。相对的,unlock就是还回那把锁的过程,也就是说,lock和unlock两行代码作为临界区的首尾,在这两个函数中间的代码对线程来说就是原子的,一次性只能通行一个线程。也要注意,锁有借有还,lock之后一定要unlock。

除此之外,还有一个函数pthread_mutex_trylock,它和lock唯一的区别是当它申请不到锁时不会一直阻塞在函数中,它申请锁后会返回一个值,让线程自主判断要不要继续申请。

②pthread_mutex_t

lock和unlock会用到一个变量,这个变量就是锁pthread_mutex_t。我们通过pthread_mutex_t mutex来定义它,它的底层定义如下,是一个联合体。

这把锁就对应内存中创建的mutex的1,每当创建一个pthread_mutex_t就会有一个1在内存中创建,可以说这个变量就是互斥锁的核心。结合前面的知识,体会它在加锁的整个流程的作用,体会为什么申请锁要传pthread_mutex_t *

下面是对上面代码的改进

通过加锁,锁间的代码在线程看来就变成原子的了,即实现了线程互斥,因此临界区的代码就不会被多个线程同时访问,也就不会出现临界资源错误了。

当创建的锁是全局或静态对象时,我们就用宏PTHREAD_MUTEX_INITIALIZER来对这个锁初始化,就像上图那样。我们之后也无需对锁进行任何其它操作。

除此之外,这个锁对象还可以是局部的,但这个时候需要手动init和destroy。

#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

int m = 10;

void *fun(void *p)
{
    pthread_mutex_t* pmutex = static_cast<pthread_mutex_t*>(p);

    pthread_mutex_lock(pmutex);
    while (m > 0)
    {
        sleep(1);
        m--;
    }
    pthread_mutex_unlock(pmutex);

    return nullptr;
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);
    pthread_t pid_1, pid_2, pid_3, pid_4, pid_5;
    pthread_create(&pid_1, nullptr, fun, (void *)&mutex);
    pthread_create(&pid_2, nullptr, fun, (void *)&mutex);
    pthread_create(&pid_3, nullptr, fun, (void *)&mutex);
    pthread_create(&pid_4, nullptr, fun, (void *)&mutex);
    pthread_create(&pid_5, nullptr, fun, (void *)&mutex);

    pthread_join(pid_1, nullptr);
    pthread_join(pid_2, nullptr);
    pthread_join(pid_3, nullptr);
    pthread_join(pid_4, nullptr);
    pthread_join(pid_5, nullptr);
    pthread_mutex_destroy(&mutex);

    printf("m = %d\n", m);

    return 0;
}

注意init函数要在还处于单线程时就使用了,如果在多线程的代码下使用,每个线程访问这行代码后都能申请到锁,那就没有意义了。同理destroy就是销毁一把锁,一定要保证在完全用完这把锁之后才能使用。

(5)加锁常见错误

①init和destroy使用位置不当,每个线程来访问都能申请到锁

②创建锁的位置不当,多个线程生成了多把锁,临界区相当于没加锁

③部分临界区在锁外,导致加锁后一定会稳定地发生错误

④每次循环后都会解锁,直接破坏了代码块的原子性,和没加锁没区别

⑤加锁解锁没有配对,导致一直有线程被阻塞在lock函数里,这使得代码永远无法结束。

(6)对互斥锁的认识

我们能够认识到锁本身也是共享资源,但由于加解锁的核心指令是原子的,使得锁本身是安全的。通过锁,我们能够将一段代码变为原子性的,使得线程访问必须一个一个进去,避免了同时访问会带来的错误。

加锁针对的是代码块,但一定不能无脑对大块代码加锁,要保证细粒度,最好只对临界区加锁。加锁会导致运行速度变慢,因为多线程执行任务时,在加锁段只能一个线程一个线程过去,好比独木桥,这座桥在保证足够用的情况下应尽量短。

我们前面讲的互斥锁都是软件实现的,加锁这件事还可以靠硬件实现。如OS一直在时钟中断,每次中断后会减去进程的时间片。当把外部中断和时钟中断禁用,不减时间片,直到执行完代码后恢复时,就实现了硬件的加锁。

2.线程同步

(1)线程互斥引发的问题

当一个线程申请到锁之后,其它线程再申请锁就会被lock,需要等待锁被还回来,当unlock后所有线程再去争夺同一把锁。这看似合理,但原本持有锁的那个线程在新一轮竞争锁的过程中最占有优势。就好比一个人还完钥匙后其它人还没反应过来的时候,他就可以再把锁拿走。这种不公平会引发效率问题,可能导致一直是同一个线程在使用锁。

要解决长期得不到锁引发的饥饿问题,需要定义一个新的规则,即被lock的线程都放到一个新的等待队列,一个线程还了锁后必须排到队列最后。这些在队列里的线程只有被唤醒后才能申请锁,这个唤醒操作每次都是针对队列头部的一个线程。这样一来,线程经过放入等待队列 -> 归还锁 -> 唤醒队头线程 -> 队头线程申请锁,就可以完美解决上述不公平问题。

于是说,多个线程访问临界资源需要线程互斥,多个线程访问临界资源还必须有顺序性,这就叫线程同步。线程互斥和同步紧密关联,互斥保证安全性,同步用于解决互斥锁带来的bug,保证系统调度更加合理和高效。

(2)pthread_cond系列函数和变量

①pthread_cond_t

这就是等待队列对应的联合体。当创建等待队列时,就需要通过pthread_cond_t cond来创建。当把线程放入等待队列的过程中,就要使用这个类型来接收线程。

它和pthread_mutex_t的创建销毁规则一模一样,全局或静态对象可用PTHREAD_COND_INITIALIZER且无需销毁,局部对象需用init和destroy手动创建销毁

②wait、signal

先看一下下面的使用,基于原先的代码进行同步改动

详细讲讲wait的详细操作,wait一定在lock和unlock之间,通常来说就紧跟在lock之后。当拿着锁的单个线程执行wait时,它要传递参数pthread_cond_t * 用于将线程加入等待队列中,还有第二个参数pthread_mutex_t * 用于还回锁。举个例子,A线程lock后拿到锁,执行wait后,A进入了等待队列并且把锁还回去。B线程就能从lock处申请到锁被放了进来,并且同样执行wait后,进入等待队列并还回锁,线程C、D亦是如此。因此,lock、wait后所有线程都被阻塞在等待队列之中。

注意lock后的线程只要有锁就不会被阻塞了,会放进去一个线程。而wait函数阻塞的线程会一直在等待队列里面阻塞,只有当对该等待队列执行signal时才会释放队头的线程。注意wait还有很重要的作用,signal后队头的线程不会马上被释放,还会被阻塞,这个线程会申请锁直到成功。所以wait后放出的线程会拿再次回这把锁,带着锁往下执行。

为什么会有这种功能?因为wait是在临界区,被阻塞在wait等待队列的线程手上都没有锁,当要往下执行时必须重新拿到锁,没有锁为何能够向下执行临界区的代码,这不就和之前的规定(进入临界区的线程拿着唯一的一把锁,目的是要保证该代码块的原子性)相悖吗?

这才是wait的线程被唤醒后一定要拿到锁才会向下执行的根本原因,注意体会逻辑的严密,理解大于记忆。

signal唯一的参数是pthread_cond_t * ,作用是唤醒相应等待队列的队头线程,只要保证指向的等待队列pthread_cond_t相同,该函数在任意地方执行均有效。

在上述代码中,所有执行fun函数的线程都被阻塞在wait队列中,主线程的signal唤醒了wait队列的队头线程,往下继续执行。unlock函数前是signal,它又唤醒了下一个线程,但这个线程必须在锁被还回去后才会被释放,也就是说unlock和signal的顺序不会改变执行逻辑,wait释放线程前都必须要申请到锁!

还有一个唤醒方法pthread_cond_broadcast( &cond ),它会立马唤醒所有队列里面的线程,参与到竞争中,一般来说用的很少,毕竟同步的目的就是保证顺序性,signal显然更严谨地保证顺序性。

下面是局部cond对象的代码

#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

int m = 10;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *fun(void *p)
{
    pthread_cond_t *pcond = static_cast<pthread_cond_t *>(p);
    pthread_mutex_lock(&mutex);
    pthread_cond_wait(pcond, &mutex);
    {
        sleep(1);
        printf("当前线程:0x%lx\n", pthread_self());
        m--;
    }
    pthread_cond_signal(pcond);

    pthread_mutex_unlock(&mutex);
    return nullptr;
}

int main()
{
    pthread_cond_t cond;
    pthread_cond_init(&cond, nullptr);

    pthread_t pid_1, pid_2, pid_3, pid_4, pid_5;
    pthread_create(&pid_1, nullptr, fun, (void *)&cond);
    pthread_create(&pid_2, nullptr, fun, (void *)&cond);
    pthread_create(&pid_3, nullptr, fun, (void *)&cond);
    pthread_create(&pid_4, nullptr, fun, (void *)&cond);
    pthread_create(&pid_5, nullptr, fun, (void *)&cond);

    sleep(1);
    pthread_cond_signal(&cond);

    pthread_join(pid_1, nullptr);
    pthread_join(pid_2, nullptr);
    pthread_join(pid_3, nullptr);
    pthread_join(pid_4, nullptr);
    pthread_join(pid_5, nullptr);

    pthread_cond_destroy(&cond);

    printf("m = %d\n", m);

    return 0;
}

执行结果是


 

扫地机器人视觉+IMU融合导航路径规划技术详解 本文介绍了基于视觉-惯性里程计(VIO)的扫地机器人自主导航系统。系统硬件由RGB摄像头、IMU传感器和编码器组成,软件架构采用分层设计,包括传感器数据采集层、数据融合层、SLAM定位层和路径规划层。核心技术包括:1)融合视觉特征提取(ORB算法)与IMU预积分的VIO;2)基于栅格地图的SLAM构建;3)结合A*全局规划和动态窗口法(DWA)局部避障的路径规划。文章提供了完整的Python实现代码,涵盖传感器数据融合、特征匹配和运动估计等关键功能模块。该系统实现了扫地机器人的精准定位和环境感知能力。 阅读详情

相关推荐

游程理论提取灾害事件特征V2---基于MATLAB语言的编程实现

游程理论,灾害事件,MATLAB

EWBA_GIS_RS_ER的博客 3792

【Linux】线程全解:概念、操作、互斥与同步机制、线程池实现

如果说进程是操作系统资源分配的基本单元,那么线程就是资源调度的基本单元,也是程序执行的基本单元。我们可以把进程看作工厂,资源就是工厂内部的原材料与各种机械设备,而线程就是工厂里的工人。因此如果进程只有一个执行流,那它也是一个线程,称之为主线程,我们在进程中创建线程其实就是在主线程下创建其他线程,线程与进程的关系如下图:说完了线程的概念,我们下面来说一下线程的操作。1.线程池的初始化:线程池通过PoolInit创建多个工作线程,这些线程通过执行thr_start来不断地获取并处理任务。2.任务队列。

谁在夜里看海.的博客 5455

手把手教你用Docker快速部署ZLMediaKit流媒体服务器(含WebRTC配置)

本文详细介绍了如何使用Docker快速部署ZLMediaKit流媒体服务器,并配置WebRTC功能。通过容器化部署,实现环境一致性、资源隔离和快速扩展,同时提供WebRTC专项配置、多协议推拉流实战测试及生产环境优化建议,帮助开发者高效搭建企业级流媒体平台。

agile9scrum的博客 202

【Linux】Linux线程互斥与同步

本文介绍了线程互斥与同步,互斥锁,条件变量,可重入与线程安全

qq_67582098的博客 2037

DANN实战:用Python手把手教你实现对抗迁移学习(附完整代码)

本文通过Python实战教程,手把手教你实现DANN对抗迁移学习模型,以解决领域偏移问题。文章详细解析了领域对抗神经网络的核心机制,包括梯度反转层和领域判别器的构建,并提供了从数据准备、模型搭建到训练评估的完整代码,帮助开发者快速掌握这一强大的无监督领域自适应技术。

net55的博客 317

Echats-wordcloud 文字云图的踩坑点【Unknown series wordCloud】

一直都是报 【ECharts Unknown series wordCloud】这玩意可能要刷脸,祝你好运吧~

Sky_Bug的博客 2418

Linux多线程——线程互斥与同步

摘要:多线程并发访问共享资源时存在数据竞争问题,需通过互斥锁和同步机制解决。 临界资源与竞争问题:多线程同时操作全局变量(如g_val--)时,由于CPU指令的非原子性,可能导致数据不一致。例如,线程切换时未完成的操作会使变量值异常。 互斥锁保护:通过pthread_mutex_t实现加锁(pthread_mutex_lock)和解锁(pthread_mutex_unlock),确保临界区代码的原子性。锁的粒度应尽量细以减少性能损失。 线程同步:条件变量(pthread_cond_t)解决线程间执行顺序问题

2401_83603768的博客 1455

告别MATLAB环境!保姆级教程:用Application Compiler把你的GUI程序打包成独立exe(2020R版本实测)

本文详细介绍了如何使用MATLAB的Application Compiler工具将GUI程序打包成独立exe文件,特别针对2020R版本进行实测。从技术原理、配置详解到打包实战与问题排查,提供完整的解决方案,帮助开发者实现无需MATLAB环境的程序分发,提升工作效率。

weixin_30571465的博客 273

Linux线程-互斥与同步

本章主要讲解学习Linux中对多线程的执行中的同步与互斥

~ 1623

OpenClaw实战系列06:持久记忆与RAG知识库构建——让AI真正“记住”你

本文介绍了OpenClaw AI助理的持久记忆与RAG知识库构建功能。OpenClaw采用"文件即真理"的独特设计,所有记忆都以Markdown文件形式存储,支持直接编辑和Git版本管理。记忆系统分为三层:长期精炼记忆(MEMORY.md)、日常日志(按日期存储)和归档记忆。文章详细说明了环境配置步骤,包括安装必要组件、创建目录结构、设置API密钥,并提供了OpenClaw主配置文件的完整参数说明。该系统实现了跨会话、跨平台的持久化记忆,使AI助理能真正理解用户需求。

PeterPan的博客 1171

【kubernetes】kubernetes kubeadm init 错误了 怎么回退?

因为虚拟机设置问题首先报错:然后我根据文章修改了核数。重启后,查看VIP运行所在机器,发现机器没有变化然后重新执行初始化后面报错了根据提供的信息,问题主要集中在 kubelet 未能成功启动控制平面组件,导致初始化过程超时。以下是可能的原因及相应的排查步骤:原因 1:kubelet 未运行或运行状态异常正如错误提示所指出,kubelet 可能未运行或处于不健康状态。请按照提示执行以下命令检查 kubelet 的状态和服务日志:根据输出结果,判断 kubelet 是否正常运行。后续一大堆错误。

九师兄 121

【Linux】线程同步和互斥

为什么要进行线程间的同步互斥?线程类似于工厂里的流水线,当某条流水线在生产时使用了工厂的公共资源时,在使用完之前,我们肯定不想其它流水线占用这个公共资源。所有我们一般在使用时都会将公共资源上锁。在使用完之后再释放。 所谓的“公共资源”,严格来讲称为临界区。线程锁的使用就是为了实现对临界区的有序访问。也就是实现线程的同步互斥

Tianzhenchuan的博客 1507

Simulink开发项1000例实战专栏--实例38:基于Simulink的周波变换器(Cycloconverter)

需求分析周波变换器(Cycloconverter),也称为频率变换器或直接变频器,是一种将输入的固定频率交流电直接转换为不同频率的交流电的电力电子设备。它广泛应用于工业领域,如大功率电机驱动、风力发电、船舶推进系统等场合。与传统的间接变频器(如交-直-交变频器)相比,周波变换器具有更高的效率和更简单的结构,适用于需要低频输出的应用场景。

xiaoheshang_123的博客 334

【linux线程(二)】线程互斥与线程同步

本篇文章着重讲解了多线程场景下的安全问题以及线程安全和可重入的联系与区别.最后讲解了为了避免造成线程饥饿问题而引进的条件变量,以及接口演示.

杭电码农-NEO的博客 1849

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Linux多线程【线程互斥与同步】

初学者在使用 多线程 并发执行任务时一定会遇到 并发访问的问题,最直观的感受就是每次运行得出的结果值大概率不一致,这种执行结果不一致的现象是非常致命,因为它具有随机性,即结果可能是对的,也可能是错的,无法可靠的完成任务,类似物理学神兽 薛定谔的猫

北 海的博客 3038

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

【Linux】线程互斥与同步

文章目录一. 什么是互斥?二. 为什么要有互斥?三. 互斥量实现互斥 一. 什么是互斥? 在介绍什么是互斥之前需要了解下面三个概念: 临界资源:多线程执行流共享的资源就叫做临界资源。 临界区:每个线程内部,访问临界资源的代码,就叫做临界区。 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成。 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用 二. 为什么要有互斥? 大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程私有栈空间

m0_51064412的博客 4204

【Linux线程】Linux多线程编程:深入理解线程互斥与同步机制

在当今这个数据驱动、高性能计算盛行的时代,多线程编程已经成为软件开发中不可或缺的一部分。Linux,作为开源世界的领头羊,其强大的多线程支持为开发者提供了广阔的舞台,让高并发、高性能的应用得以实现。然而,多线程编程也是一把双刃剑,它在带来性能提升的同时,也引入了线程安全、资源竞争等复杂问题

EterNity_TiMe_的计算机学习之路 2814

【Linux学习】线程互斥与同步

线程互斥,临界资源,互斥锁,死锁问题,避免死锁,多线程编程,临界线程互斥是一种同步机制,用于控制对共享资源的访问,以确保在任意给定的时刻只有一个线程可以访问该资源。在多线程编程中,当多个线程同时竞争访问某个共享资源时,如果没有适当的同步机制,可能会导致竞争条件和数据不一致性的问题。线程互斥通过引入互斥锁等机制,使得在任意时刻只能有一个线程持有资源的访问权限,从而避免了竞争条件和数据不一致性的发生。进程线程间的互斥相关背景概念多线程执行流共享的资源叫做临界资源。每个线程内部,访问临界资源的代码,就叫做临界区。

Chris在Coding的博客 1457

Linux之线程同步与互斥

Linux之线程同步与互斥

2302_81120572的博客 1584

【Linux线程】——线程同步&&线程互斥

在计算机系统这个庞大而复杂的数字宇宙中,线程如同繁星般繁多且活跃。它们各自在自己的轨道上运行,承载着数据处理、任务执行的重任。然而,就像现实世界中的天体有时会相互靠近、碰撞,线程之间也会因为资源的共享和交互而产生一系列的“碰撞”问题。在这样的背景下,线程同步和互斥就像是这个宇宙中的引力法则和空间规则,保障着线程们的有序运行,避免混乱和冲突。让我们一同踏上探索线程同步与互斥的奇妙之旅,揭开它们神秘的面纱。Linux线程的互斥与同步是一个充满挑战与魅力的技术领域。

2402_85267481的博客 1458

【Linux】:多线程(互斥 && 同步 && 生产消费者模型)

该篇文章主要是关于线程互斥(mutex)、线程同步(cond)的详细讲述,还实现了互斥量和条件变量的简单封装,最后后面还讲述了关于生产消费者模型(BlockingQeeue)的实现

island1314的博客 3036

【Linux】线程ID与互斥、同步(锁、条件变量)

💬 hello!各位铁子们大家好哇。今日更新了Linux线程的内容。

qinjh_的博客 4078

Linux线程——线程互斥、同步、POSIX信号量

本文探讨了线程互斥与同步的概念及其实现方法。文章首先通过模拟抢票程序展示了多线程访问共享资源时可能出现的数据不一致问题,如票数出现负值的情况。分析指出,这是由于共享资源访问未加保护导致的线程安全问题。 为解决这一问题,文章介绍了使用pthread库中的互斥量(mutex)来保护共享资源。通过加锁机制,确保同一时间只有一个线程能访问关键代码段,从而避免了数据竞争。文章详细解析了ticket自减操作的汇编实现,结合操作系统分时调度的特点,解释了数据不一致的根本原因。 此外,文章还简要提及POSIX信号量的使用,

2301_79705195的博客 1447

【Linux】线程同步与互斥

互斥量、条件变量、信号量、生产者消费者模型、线程池、日志

weixin_69380220的博客 1781

Linux内核视角:线程同步与互斥的原理、实现与锁优化策略

在前两篇文章中,我们已经对线程的相关概念及基本操作进行了深入介绍。在本篇中,我们将深入探讨编写多线程程序时非常重要的一个环节——保证线程的同步互斥,以此确保程序执行结果的正确性。线程互斥是一种重要的并发控制机制,其核心在于同一时刻仅允许单个线程访问共享资源。借助互斥机制,当某个线程正在对共享内存进行读写操作时,其他线程会被暂时阻止访问同一资源。这一约束能够有效规避竞态条件——即多个线程同时访问和修改共享数据可能引发的不可预测行为,从而确保数据的一致性和程序执行的正确性。

2301_80774875的博客 2443

【进程与线程】Linux 线程、同步以及互斥

线程是操作系统与多线程编程的基础知识。系统为每个用户进程创建一个来描述该进程:该结构体中包含了一个指针指向该进程的虚拟地址空间映射表:实际上和地址空间映射表一起用来表示一个进程。由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大,因此,为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程。在同一个进程中创建的线程共享该进程的地址空间,Linux里同样用来描述一个线程。线程和进程都参与统一的调度。接下来我们会详细的介绍线程共享资源和私有资源,还有线程库如。

猫猫的小茶馆 2240

Linux线程互斥与同步(下)(30)

Hello,现在到同步啦!我们下一篇将继续揭晓!

tan180°的博客 1128
上一篇: Linux相关概念和易错知识点(29)(线程相关性质和操作、线程封装)
下一篇: Linux相关概念和易错知识点(31)(生产者-消费者模型、认识信号量、线程安全)
-SGlow-
博客等级 码龄3年 1513粉丝 118原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值