线程同步与互斥

线程的互斥与同步 什么是互斥?什么是同步? 互斥:一个资源一次只能被一个访问者使用(保证访问数据,唯一访问性) 举个例子:你去上厕所,门一开,人进去,门锁上,在你上厕所期间别人不能打扰 同步:当多个访问者一起工作时并对每个访问者访问的时序有一定限制(保证访问数据,时序访问性) 举个例子:你上完厕所,刚出来,本来应该由别人了,但是你此时又进去,你反复的进去出来,导致别人都上不了厕所。因此同步就是保证时序性的,... 阅读详情

线程互斥

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

  • 共享资源: 在多线程执行流中,能被多线程看到的资源就称为共享资源。
  • 临界资源:多线程执行流中,被保护的共享资源就称为临界资源。
  •  临界区:每个线程内部,访问临界资源的代码段,称为临界区。
  • 互斥:在任何时刻,互斥确保有且只有一个执行流进入临界区,访问临界资源,从而对临界资源起到保护作用。
  •  原子性(后续讨论如何实现):指不会被任何调度机制打断的操作,该操作只有两种状态,要么完成,要么未完成。

通过一段代码认识一下上面的概念。

简单模拟抢票机制:

#include<iostream>
#include<pthread.h>
#include<string>
#include<cstdio>
#include<unistd.h>
int tickets=1000;
void* route(void* args)
{
    std::string name=static_cast<const char*>(args);
    while(true)
    {
        if(tickets>0)
        {
            usleep(1000);
            printf("%s 抢到了 %d 号票\n",name.c_str(),tickets);
            tickets--;
        }
        else
        break;
    }
    return (void*)0;
}
int main()
{
    pthread_t p1,p2,p3,p4;
    pthread_create(&p1,nullptr,route,(void*)"thread_1");
    pthread_create(&p2,nullptr,route,(void*)"thread_2");
    pthread_create(&p3,nullptr,route,(void*)"thread_3");
    pthread_create(&p4,nullptr,route,(void*)"thread_4");

    pthread_join(p1,nullptr);
    pthread_join(p2,nullptr);
    pthread_join(p3,nullptr);
    pthread_join(p4,nullptr);
    return 0;
}

运行结果:

在上面代码中tickets是个全局变量,可以被所有线程看到,所以该变量是个共享资源。

当我们运行结束后,我们发现,票被抢到了负数,为什么会出现这种现象呢?

在CPU中,只有一组寄存器,寄存器中只能保存当前执行流的上下文。在这个抢票的代码中,票的数据保存在物理内存中,当某个线程被唤醒,那么CPU中的寄存器就会拷贝票的数据,但是无论是线程还是进程,都有时间片,当时间片的时间到了,寄存器就要切换下一个线程的上下文,当线程再次切换回来的时候,线程就直接从被切走的地方开始执行。在上面这段代码中,如果线程在后面还有几张票的时候,都在if判断完后,因为时间片的时间到了,然后被切走了,线程再次被唤醒时,就可以直接打印,让票的数量再减减,抢到的票就是负数了。

要解决以上问题,需要做到三点:

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
  • 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
  •  如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。

要做到这三点,本质上就是需要⼀把锁。Linux上提供的这把锁叫互斥量。

互斥量mutex

互斥量的接口

初始化互斥量

初始化互斥量有两种方法:

  • 方法1:静态分配:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

  • 方法2:动态分配:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, constpthread_mutexattr_t *restrict attr);
参数:
mutex:要初始化的互斥量
attr:NULL

注:动态初始化的条件变量必须调用销毁函数!

销毁互斥量

销毁互斥量注意事项

  • 使用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_mutex_lock时可能遇到的情况

  • 互斥量处于未锁状态:该函数会将互斥量锁定,同时返回成功
  • 互斥量已被锁定:发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量但没有竞争到互斥量,那么pthread_mutex_t mutex调用会陷入阻塞(执行流被挂起),等待互斥量解锁

改进上面的抢票代码:

#include<iostream>
#include<pthread.h>
#include<string>
#include<cstdio>
#include<unistd.h>
int tickets=1000;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex;
void* route(void* args)
{
    std::string name=static_cast<const char*>(args);

    while(true)
    {
        pthread_mutex_lock(&mutex);
        if(tickets>0)
        {
            usleep(1000);
            printf("%s 抢到了 %d 号票\n",name.c_str(),tickets);
            tickets--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
       
    }
    return (void*)0;
}
int main()
{
    pthread_t p1,p2,p3,p4;
    pthread_mutex_init(&mutex,nullptr);
    pthread_create(&p1,nullptr,route,(void*)"thread_1");
    pthread_create(&p2,nullptr,route,(void*)"thread_2");
    pthread_create(&p3,nullptr,route,(void*)"thread_3");
    pthread_create(&p4,nullptr,route,(void*)"thread_4");

    pthread_join(p1,nullptr);
    pthread_join(p2,nullptr);
    pthread_join(p3,nullptr);
    pthread_join(p4,nullptr);
    pthread_mutex_destroy(&mutex);
    return 0;
}

在上面代码中,因为对tickets变量进行了保护,所以tickets变量就是临界资源;被加锁和解锁代码的部分就是临界区。

互斥量实现原理探究

为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换。由于只有一条指令,保证了原子性。即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时,另一个处理器的交换指令只能等待总线周期。

现在我们把lock 和unlock的伪代码改写为基于这种硬件原语的实现:

mutex值为0表示该锁正在被使用,还未归还,1表示该锁空闲,可使用。

lock:
        movb $0, %al           # 将0值移动到AL寄存器
        xchgb %al, mutex       # 原子交换AL寄存器和mutex变量的值
        if (al > 0) {          # 检查交换后AL寄存器的值
            return 0;          # 等于0表示加锁成功
        } else {
            挂起等待;           # 大于0表示锁已被占用,挂起当前线程
        }
        goto lock;           # 被唤醒后重新尝试获取锁

unlock:

                movb $1, mutex             #将1值移交给mutex变量

                唤醒等待Mutex的线程

                return 0;

理解

临界区就好比是只能容纳一个人的图书馆自习室,假设自习室是有锁的,锁要拿钥匙开的,那么钥匙就相当于互斥量,而自己拥有钥匙,在自习室里面学习,那么这种行为就是加锁;把钥匙归还给图书馆,留给别人用,那么这种行为就是解锁。如果拥有自习室钥匙的同学(小明)总想离开房间一会,但小明还想待会回来学习,小明就把钥匙拿走了,其他同学得不到这个钥匙,只能在外面一直等小明归还钥匙。这里面的小明就相当于正在执行临界区的线程。但是如果小明一直不归还钥匙,那么其他同学就无法进入自习室,其他同学只能等待小明归还钥匙,这就会导致“饥饿”问题。

要解决以上的问题,那么就需要做到以下三点:

  • 互斥进入
  • 凡是从自习室内出来的人,归还钥匙后,不能立即申请
  • 外部的人必须排好队,出来的人必须排在队伍的尾部

要做到以上三点,本质上就是需要线程同步。

线程同步

条件变量

续上面的例子:

主动轮询(没有条件变量):

同学们在自习室外不停地敲门:"钥匙有了吗?钥匙有了吗?"

  • 每个同学都频繁尝试申请钥匙

  • 图书馆管理员(操作系统)忙于处理这些请求

  • 同学们自己也很累,还制造了很多噪音(消耗CPU)

条件变量等待(高效方式):

同学们在门外安静地休息(线程阻塞)

  • 小明归还钥匙时按门铃

  • 同学们被唤醒后才去申请钥匙

  • 大家都不累,系统也很安静

条件变量让线程在条件不满足时被动等待,而不是主动轮询。主动轮询就是一直申请互斥量,这样会让CPU一直去检测执行条件是否满足,会耗费CPU。

线程同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题。

条件变量函数

初始化

静态初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

动态初始化:

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t*restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL

销毁

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<pthread.h>
#include<unistd.h>
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
void* route(void* args)
{
    std::string name=static_cast<const char*>(args);
    while(1)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond,&mutex);//阻塞等待,其他线程等待通知才能进入临界区
        std::cout<<name.c_str()<<"进入临界区"<<std::endl;
        pthread_mutex_unlock(&mutex);
    }
    return (void*)0;
}
int main()
{
    pthread_t t1,t2;
    pthread_create(&t1,nullptr,route,(void*)"thread_1");
    pthread_create(&t2,nullptr,route,(void*)"thread_2");
    
    sleep(2);
    while(1)
    {
        pthread_cond_broadcast(&cond);
        sleep(1);
    }
    pthread_join(t1,nullptr);
    pthread_join(t2,nullptr);
    return 0;
}

为什么 pthread_cond_wait 需要互斥量?

  • 当线程A进入临界区时,会先给进程A加锁,但是如果线程A没有满足条件变量,那么线程A就要加入到等待队列,但是线程A手里还拿着锁,没有把锁释放,其他进程就拿到不这个锁,就会造成死锁现象。
  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有另一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好地通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。

按照上面的说法,我们设计出如下的代码:先上锁,发现条件不满足,解锁,然后等待在条件变量上不就行了,如下代码:

// 错误实现:非原子操作
pthread_mutex_lock(&mutex);          // 1. 加锁
while (condition == false) {         // 2. 检查条件
    pthread_mutex_unlock(&mutex);    // 3. 解锁
    // 🚨 危险区域:可能错过通知!
    pthread_cond_wait(&cond);        // 4. 等待(没有互斥量保护)
    pthread_mutex_lock(&mutex);      // 5. 重新加锁
}
// 处理工作...
pthread_mutex_unlock(&mutex);

在上面的代码中,如果线程A去检查临界区,去临界区前会对该线程进行加锁,但加了锁后发现不满足条件变量,线程A进行解锁,但是在解锁后正在要加入到阻塞队列的过程中,其他进程可能申请得到了互斥量,那么线程A就会进入永久等待中。

当线程解锁后,内核就会发出信号,让其他线程去申请,但是信号递达后,就会消失,所以线程还没有进入阻塞队列中,信号就已经消失了,那么线程就会进入永久等待中。

所以等待必须是一个原子操作,pthread_cond_wait的原子性保证:先入队,后释放锁,发出通知。

正确原子操作

// 正确实现:原子操作
pthread_mutex_lock(&mutex);              // 1. 加锁
while (condition == false) {             // 2. 检查条件
    pthread_cond_wait(&cond, &mutex);    // 3. 原子操作:
                                         //    - 释放锁
                                         //    - 等待通知  
                                         //    - 重新获取锁
}
// 处理工作...
pthread_mutex_unlock(&mutex);            // 4. 解锁

条件变量使用规范

等待条件代码

pthread_mutex_lock(&mutex);
while (条件为假) //if??
pthread_cond_wait(&cond, &mutex);
修改条件
pthread_mutex_unlock(&mutex);

给条件发送信号代码

pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

线程安全与重入问题

概念

线程安全:就是多个线程在访问共享资源时,能够正确地执行,不会相互干扰或破坏彼此的执行结果。一般而言,多个线程并发同一段只有局部变量的代码时,不会出现不同的结果。但是对全局变量或者静态变量进行操作,并且没有锁保护的情况下,容易出现该问题。

重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。

学到现在,其实我们已经能理解重入其实可以分为两种情况:

  • 多线程重入函数
  • 信号导致一个执行流重复进入函数(在进程间信号讲过)

常见的线程不安全的情况

  • 不保护共享变量的函数;
  • 函数状态随着被调用,状态发生变化的函数;
  • 返回指向静态变量指针的函数;
  • 调用线程不安全函数的函数。

常见的不可重入的情况

  • 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的;
  • 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构;
  • 可重入函数体内使用了静态的数据结构。

常见的线程安全的情况

  • 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的;
  • 类或者接口对于线程来说都是原子操作;
  • 多个线程之间的切换不会导致该接口的执行结果存在二义性。

常见的可重入的情况

  • 不使用全局变量或静态变量;
  • 不使用malloc或者new开辟出的空间;
  • 不调用不可重入函数;
  • 不返回静态或全局数据,所有数据都由函数的调用者提供;
  • 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。

总结

  1. 函数是可重入的,线程就是安全的。
  2. 线程安全不一定是可重入的。因为如果这个重入函数带锁,而且上完锁之后被信号中断了,执行流就把锁带走了,等到解决完信号问题回来的时候,又申请锁,但系统不能识别锁,所以这把锁就不见了,就会导致死锁问题。

注意:

  • 如果不考虑信号导致一个执行流重复进入函数这种重入情况,线程安全和重入在安全角度不做区别。
  • 线程安全侧重说明线程访问公共资源的安全情况,表现的是并发线程的特点
  • 可重入描述的是一个函数是否能被重复进入,表示的是函数的特点

常见锁

死锁

死锁是指在一组进程(或线程)中,每个进程都持有至少一个资源,并且都在等待获取另一个被该组中其他进程所占用的资源。由于没有一个进程能够主动释放自己已占有的资源,这组进程将永远处于互相等待的状态。

为了方便表述,假设现在线程A,线程B必须同时持有锁1和锁2,才能进行后续资源的访问。

申请一把锁的时候,是具有原子性的,但是多把锁就不是了。

形成死锁的四个充分必要条件

互斥条件:一个资源每次只能被一个执行流使用。

请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放。

  • 就好比两个人都只有五毛钱的情况下,想吃棒棒糖(一块钱一个),两个人都想吃,所以两个人都不愿把自己身上的五毛钱给对方,但是同时又想要对方的五毛钱。这里的两个人就好比是两个线程,棒棒糖就好比是临界资源,钱就好比是锁。

不剥夺条件:一个执行流已获得的资源,在未使用完之前,不能强行剥夺。

  • 和上面情景一样,虽然我想吃,但是我不会去抢别人的钱。

循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系。

  • 两个人就在互相等待对方能把钱给自己,这就是循环等待。多线程之间也存在。

避免死锁

只要破环形成死锁的四个条件中的一个就可以避免。

对于条件1:不用锁就不会产生死锁。

对于条件2:

核心思想:让线程在开始执行前就一次性申请它所需要的所有资源,而不是在运行过程中逐步申请。把两个锁“绑定”在一起。

方法:在 C++ 中,可以用是std::lock(lock1,lock2,……)来同时锁定多个锁,避免死锁。

#include <mutex>
#include <thread>

std::mutex mtx1, mtx2;

void function_with_two_locks() {
    // 同时锁定 mtx1 和 mtx2,避免死锁
    std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);
    std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);
    std::lock(lock1, lock2); // 同时锁定

    // ... 临界区操作两个资源 ...
}

对于条件3:

核心思想:如果一个线程已经持有了一些资源,但无法立即获得新的资源,那么它必须释放已经获得的所有资源,以后需要时再重新申请。

方法:使用带超时机制的锁(如pthread_mutex_timelock)。如果一个线程在等待新锁时超时,它就主动释放自己持有的所有锁,然后等待一段时间后重试。

对于条件4:

核心思想:对系统中的所有资源进行统一编号,强制线程按照严格的顺序(升序或降序)来申请资源。

STL,智能指针和线程安全

STL中的容器是否是线程安全的?

不是。STL的设计初衷是将性能挖掘到极致,而一旦涉及到加锁保证线程安全,会对性能造成巨大的影响,而且对于 不同容器,加锁的方式不同,性能可能也不同(例如hash表中的锁表和锁桶),因此STL默认不是线程安全的,如果需要在多线程环境下使用,往往需要调用者自行保证线程安全。

智能指针是否是线程安全的?

对于unique_ptr,由于只是在当前代码块范围内生效,因此不涉及线程安全问题。

对于shared_ptr,多个对象需要共用一个引用计数变量,所以会存在线程安全问题,但是标准库实现的时候考虑到这个问题,基于原子操作的方式保证了shared_ptr能够高效,原子的操作引用计数。

注:shared_ptr 保证的是引用计数的线程安全,不是指向对象的线程安全,也不是同一个 shared_ptr 实例的线程安全。

Python 线程同步与互斥 什么是并发?在操作系统中,指一个时间段内有几个程序都处于已启动到运行结束之间的状态,并且这几个程序都是在同一个处理机上运行的,但任一个时间点却只有一个程序在处理机上执行。 注意并发与并行并不是同一个概念。并发是指一个时间段内同时运行,表示的是一个区间,而并行是指在同一个时间点上都在运行,是一个点,并且并发在同一时间点上只能有一个程序在运行。 线程同步与互斥 并发 进程与线程区别 阅读详情

相关推荐

Linux 线程互斥与同步

和 pthread_cond_signal 不同,pthread_cond_signal 只唤醒 一个 等待的线程,而 pthread_cond_broadcast 唤醒 所有等待的线程。:当线程调用 pthread_cond_wait() 时,它会自动释放所持有的互斥锁,并将自己置于等待状态,直到被另一个线程用 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒。:条件变量的作用是协调线程的执行,确保某些条件为真时,线程才会继续执行。

yuzhebalabala的博客 1317

线程的同步与互斥

线程的同步与互斥: 互斥:当一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。如:当线程A在使用打印机时,其他线程都需要等待。 同步:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。如:A任务的运行依赖B任务产生的数据。 同步保证了线程运行的顺序性,互斥保证了线程运行的安全性。 同步是一种更为复杂的互斥,而互斥是一种特殊的同步。 互斥具有唯一性和

Nessie_zhao的博客 4008

医疗设备安规入门:一张图搞懂BF型设备的MOOP/MOPP绝缘路径(附GB 9706.1附录解析)

本文深入解析了BF型医疗设备的MOOP/MOPP绝缘路径设计,结合GB 9706.1标准附录J的实战应用,帮助工程师快速掌握医疗设备安规要点。通过典型绝缘路径图分析,详细对比了MOOP与MOPP的防护差异,并提供了海拔修正、漏电流测试等关键设计指南,助力医疗设备安全合规。

weixin_30633405的博客 381

线程的同步与互斥:互斥锁

什么是线程的同步与互斥? 同步:假设现有线程A和线程B,线程A需要往缓冲区写数据,线程B需要从缓冲区读数据,但他们之间存在一种制约关系,即当线程A写的时候,B不能来拿数据;B在拿数据的时候A不能往缓冲区写,也就是说,只有当A写完数据(或B取走数据),B才能来读数据(或A才能往里写数据)。这种关系就是一种线程的同步关系。 互斥:对于线程A和线程B来讲,在同一时刻,只允许一个线程对临界资源进行操作,即当

YANG 2万+

Agent本地部署教程详细步骤

在开始之前,你需要明确你要部署的Agent是什么。如果你需要部署其他Agent,请参考其官方文档调整步骤。

weixin_39673931的博客 2494

开源实战 | 基于SolidWorks与Matlab的并联索驱机器人轨迹规划全流程

本文详细介绍了基于SolidWorks与Matlab的并联索驱机器人轨迹规划全流程,涵盖从基础概念到实战技巧的各个环节。通过SolidWorks建模、Matlab运动学实现及轨迹优化策略,展示了如何实现高精度轨迹规划,适用于摄影、医疗等领域的机器人应用。

weixin_28331555的博客 402

线程互斥、同步

多线程执行流共享的资源叫做临界资源每个线程内部访问临界资源的代码,被称为临界区任何时刻,互斥保证有且只有一个执行流进入临界区访问临界资源,通常对临界资源起保护作用不会被任何调度机制打断的操作,该操作只有两态:要么完成,要么未完成下面模拟实现一个抢票系统,将记录票的剩余张数的变量定义为全局变量,主线程创建四个新线程进行抢票,当票被抢完后这四个线程自动退出//抢票所耗费的时间

GG_Bruse的博客 7924

ApolloScape 数据集:YOLOv10 + UI界面实现与应用

是由百度Apollo团队发布的一个大规模自动驾驶数据集,涵盖了车辆、行人、骑行者等多种交通元素,旨在为自动驾驶技术的发展提供丰富的数据支持。数据集包含多种复杂的场景,例如城市道路、交叉口、车辆聚集等,能够帮助研究人员和开发者在多种现实环境下测试和验证目标检测模型。YOLO(You Only Look Once)系列算法是目前最流行的目标检测算法之一,因其高效、准确以及实时性强的特点被广泛应用于计算机视觉领域。YOLOv10是YOLO系列中的最新版本,在性能和效率上进行了多方面的优化。速度快。

YOLO 926

线程及线程的同步互斥

目录线程的简单介绍信号量互斥量条件变量1.线程的简单介绍 线程的简单介绍 信号量 互斥量 条件变量 1.线程的简单介绍

weixin_45738899的博客 2970

【Bug已解决】Codex CLI 运行时出现 command not found: codex 解决方案

方案适用场景推荐指数修复 PATH 配置最常见的基础排查方向⭐⭐⭐⭐⭐检查原生二进制完整性PATH 正常但执行仍然异常⭐⭐⭐⭐统一包管理器混用多种包管理器导致混乱⭐⭐⭐⭐确认安装脚本未被跳过CI/CD 或企业策略环境⭐⭐⭐⭐排查网络下载渠道企业内网/受限网络环境⭐⭐⭐需要留意 warning 中是否涉及脚本执行失败、网络超时重试等信息,这类 warning 往往是后续问题的前兆,建议出现明显的下载/网络相关警告时就主动重新安装一次确认。Codex CLI 报。

一键难忘的博客 1万+

(一)深入理解Bluez协议栈之蓝牙Mesh的支持(1/5)

BlueZ是Linux官方蓝牙协议栈。它是一个基于GNU General Public License (GPL)发布的开源项目,从Linux 2.4.6开始便成为Linux 内核的一部分。如果你是linux平台开发蓝牙的的工程师,肯定会接触过Bluez协议栈,hciconfig,hciattach,hciconfig等工具都是很好用的工具。

3321

[Linux]线程的互斥与同步

在多线程编程中,互斥与同步机制至关重要。互斥,是指同一时刻只允许一个线程访问共享资源,防止数据竞争。实现互斥常依赖互斥锁接口,通过加锁、解锁操作控制对资源的访问,其本质是线程获取和释放对共享资源的独占权。然而,若使用不当,可能产生死锁,即多个线程相互等待对方释放资源而陷入僵局。同步则着重于线程间的协作,按特定顺序访问资源。条件变量是同步的重要工具,通过其接口,线程可在条件满足时被唤醒,从而实现高效的线程协作,保障多线程程序的正确运行。

m0_74207969的博客 1578

什么是DHCP Snooping?如何配置DHCP Snooping?

功能描述防护类型防御伪造 DHCP 服务器、避免地址冲突、增强网络安全性使用场景企业园区网、校园网、大型局域网配合机制IP Source Guard、DAI、防止 ARP 欺骗部署难度⭐⭐(简单)“DHCP Snooping,是内网 DHCP 安全防护的第一道门神。” 🛡️。

网络技术联盟站 1264

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

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

Chris在Coding的博客 1457

逆强化学习:从专家示范中学习策略

逆强化学习(Inverse Reinforcement Learning,简称IRL)是强化学习(Reinforcement Learning,简称RL)的一个子领域,它的目标是从专家的行为中学习策略。在传统的强化学习中,智能体通过与环境交互,根据奖励信号来学习一个策略。而在逆强化学习中,我们没有直接的奖励信号,而是通过观察专家的行为来推断出奖励函数,从而学习到一个与专家类似的策略。逆强化学习作为强化学习的一个重要子领域,在许多实际应用场景中都取得了显著的成功。

AI天才研究院 1447

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Linux线程同步与互斥:【线程互斥】【线程同步】【线程池】

生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区, 平衡了生产者和消费者的处理能力。因为我在当前的进程没有进行释放锁的操作,我是持有锁被切换的,相当于拿着钥匙,钥匙不给其他线程,其他线程也只能干等着,等我回来释放锁,其他线程才能展开锁的竞争,进入临界区。• 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完 成线程之间的交互。

2301_81699364的博客 1282

efm32 入门.pdf

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

Linux线程-互斥与同步

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

~ 1623

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

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

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

2401_83603768的博客 1455

【Linux】线程的同步与互斥

本文深入探讨了线程互斥与同步的概念及其在多线程编程中的应用。首先,文章回顾了互斥的基本概念,包括临界资源、临界区、互斥和原子性,并通过抢票系统的代码示例展示了多线程环境下数据不一致的问题。接着,文章引入了互斥锁(Mutex)的概念,详细介绍了其接口、使用方法以及如何通过加锁机制解决数据竞争问题。此外,文章还探讨了条件变量(Cond)的使用,解释了如何通过条件变量实现线程的同步,避免线程饥饿现象。最后,文章介绍了生产者/消费者模型,并通过阻塞队列(BlockingQueue)的实现展示了如何在实际应用中结合互

2301_81577798的博客 1万+

Linux线程同步与互斥

上图中,线程1因为某些原因需要线程切换,进入等待队列。那么此时线程1需要把自己的上下文数据带走,其实就是把寄存器当中保存的值带走,并且没有对内存交换的0进行写回,也就是说此时内存中的mutex是0,那么线程2在交换mutex到寄存器当中,就会进行状态检测,此时检测到状态为0,说明当前已经有人占用锁了,则线程2进入到挂起状态,后来的线程依旧会如此,直到第一个线程执行完毕将锁释放。大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。

欢迎来到阿熊的blog~~ 1159

Linux_线程的同步与互斥

在Linux下,线程是一个很重要的概念,他可以提高多执行流的并发度,而同步与互斥是对线程的一种约束行为,比如当多个线程都访问同一个资源时,若不对该资源加以保护则会导致意料之外的错误。具体的保护措施是让线程访问共享资源时具有互斥性,即当一个线程访问时别的线程无法访问,通常用互斥锁来实现。而同步是为了让多个线程具有一定的顺序来访问共享内存,保障每个线程访问资源的机会是一样的。

安权_code的博客 2053

线程同步与线程互斥

1、获取线程ID功能:获取线程号返回值:线程ID2、分离线程函数功能:使线程分离,线程退出时让系统自动回收线程资源参数:线程ID子线程在退出的时候,主线程没有给子线程回收资源的话,线程会变成僵尸线程。使用方式:1)在主线程中调用pthread_detach(tid);2)在子线程中调用pthread_detach(pthread_self());注意:1)如果线程已经设置了分离状态则再调用pthread_join时就会失败。

WBGZSL的博客 1456

【Linux】线程互斥与同步

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

m0_51064412的博客 4204
上一篇: Linux:线程概念及控制
下一篇: 生产者消费者模型
噜啦噜啦嘞好
博客等级 码龄3年 212粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值