【Linux】锁|死锁|生产者消费者模型

【Linux】---互斥与同步、死锁与饥饿、实现生产者消费者模型、常见锁类型 目录梳理前言互斥与同步互斥量与条件变量信号量线程池死锁与饥饿死锁条件避免死锁避免饥饿常见特殊锁乐观锁悲观锁自旋锁读写者锁公平与非公平锁 梳理前言 进程线程间的互斥相关背景概念: 临界资源:多线程执行流共享的资源就叫做临界资源 临界区:每个线程内部,访问临界资源的代码,就叫做临界区 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成。 死锁:两个或两个以上的进程(LWP)都在等待互相释放自 阅读详情

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

访问互斥

引起互斥常见的原因:多线程访问全局资源或静态资源,多线程访问文件数据,多线程使用共享数据引发异常

例如两个线程都在对一个全局变量num=0执行加法操作。执行加法需要有三步操作,从寄存器中取出操作数,在算术逻辑单元进行加1,将结果写回寄存器。如果让两个线程执行加法操作,当其中一个线程A的从寄存器中取出了操作数,另外一个线程B已经完成了加1,此时线程A对操作数完成加1,再放回寄存器仍是1。这就会导致两个线程访问全局资源产生互斥。

​

通过下面一个代码可以观察到这一现象:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

int number;
void * thread_job(void * arg)
{
    int tmp;
    for(int i=0;i<5000;++i)
    {
        tmp=number;
        printf("Thread 0x%x ++number:%d\n",(unsigned int)pthread_self(),++tmp);
        number=tmp;
    }
}


int main()
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread_job,NULL);
    pthread_create(&tid,NULL,thread_job,NULL);
    while(1) sleep(1);
    return 0;
}

两个线程各对一个全局变量执行5000次加法,结果不可控

​

锁

互斥锁

互斥锁可以避免多线程同时访问资源,避免资源异常,结果异常。在读写全局数据时加上锁,读写完成后解锁。

pthread_mutex_t lock 互斥锁的数据类型

关于锁常用的函数:

pthread_mutex_init(&lock,NULL) 互斥锁初始化

phtread_mutex_lock(&lock) 上锁

pthread_mutex_unlock(&lock) 解锁

pthread_mutex_destory(&lock)释放互斥锁

两个线程如果同时使用一个锁,会发送锁被占用,一个线程挂起等待。

​

加锁的位置很重要,可能改变线程执行流程

​

通常情况是全局资源读写时需要上锁

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

pthread_mutex_t lock;//锁需要定义成全局变量,才会对多线程生效
int number;
void * thread_job(void * arg)
{
    int tmp;
    for(int i=0;i<5000;++i)
    {
        pthread_mutex_lock(&lock);
        tmp=number;
        printf("Thread 0x%x ++number:%d\n",(unsigned int)pthread_self(),++tmp);
        number=tmp;
        pthread_mutex_unlock(&lock);
    }
}


int main()
{
    pthread_t tid;
    pthread_mutex_init(&lock,NULL);
    pthread_create(&tid,NULL,thread_job,NULL);
    pthread_create(&tid,NULL,thread_job,NULL);
    while(1) sleep(1);
    return 0;
}

​

惊群问题

多线程争抢资源,因资源有限,未抢到的线程付出了无意义的系统开销

当一个线程的互斥锁使用完毕后,其他线程都会争抢这个锁, 被唤醒并争夺资源 ,从挂起态回到运行态。当其中一个线程争抢到锁后,其余线程又回到挂起态。造成了无意义的资源浪费和系统开销。

互斥锁等待队列是操作系统和线程库中用于管理等待互斥锁的线程的一个数据结构。它主要解决多线程环境中对共享资源的竞争问题,确保只有一个线程在某一时刻可以访问共享资源,从而避免数据竞争和不一致性。

对优先级高的线程使用标志标记

​

但是如果正在使用锁的线程释放锁后仍有时间片,它还会占用锁

旋转锁

提高锁的利用率,当多个进程需要同时使用锁时,其他线程不会挂起等待,一直处于运行态R。

读写锁

互斥锁不允许多个线程同时访问资源,资源的利用率较低,读写锁可以解决这个问题,它支持一个线程写资源,多个线程可以同时读资源,提高资源利用率。

特点:读共享,写独占,读写互斥

pthread_rwlock_t lock; 读写锁

pthread_rwlock_init(&lock,NULL) 读写锁初始化

pthread_rwlock_destroy(&lock)释放锁

pthread_rwlock_rdlock(&lock) 读锁上锁

pthread_rwlock_wrlock(&lock) 写锁上锁

pthread_rwlock_unlock(&lock) 解锁

下面是读写锁使用的demo程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

int number;
pthread_rwlock_t lock;
void *thread_read(void *arg)
{
    while(1)
    {
        pthread_rwlock_rdlock(&lock);
        printf("Read Thread 0x%x Num=%d\n",(unsigned int)pthread_self(),number);
        pthread_rwlock_unlock(&lock);
        usleep(100000);
    }

}

void *thread_write(void *arg)
{
    while(1)
    {
        pthread_rwlock_wrlock(&lock);
        printf("Write Thread 0x%x Num=%d\n",(unsigned int)pthread_self(),++number);
        pthread_rwlock_unlock(&lock);
        usleep(100000);
    }
}
int main()
{

    pthread_t tids[8];
    pthread_rwlock_init(&lock,NULL);
    int i;
    for(i=0;i<3;++i)
    {
        pthread_create(&tids[i],NULL,thread_write,NULL);
    }
    for(i;i<8;++i)
    {
        pthread_create(&tids[i],NULL,thread_read,NULL);
    }
    while(i--)
    {
        pthread_join(tids[i],NULL);
    }

    pthread_rwlock_destroy(&lock);
        exit(0);

}

运行结果:写的结果各不相同,读的结果都相同

​

进程互斥锁

通过修改互斥锁属性可以将线程互斥锁变为进程互斥锁,在进程间使用。

pthread_mutexattr_t互斥锁属性类型,默认情况下属性中均为线程互斥锁

pthread_mutexattr_setpshared(pthread_mutexattr_t* attr,PTHREAD_PROCESS_PRIVATE | PTHREAD_PROCESS_SHARED)修改锁属性中的成员

下面是多进程共享内存,使用了互斥锁来保护共享资源的demo程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>

typedef struct {
    int number;
    pthread_mutex_t lock;
} shared_t;

int main(void) {
    int fd;
    fd = open("PROCESS_LOCK", O_RDWR | O_CREAT, 0664);
    ftruncate(fd, sizeof(shared_t)); // 扩展文件大小
    
    shared_t *ptr = NULL;
    ptr = mmap(NULL, sizeof(shared_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    pid_t pid;
    
    // 初始化共享资源
    ptr->number = 0;
    
    pthread_mutexattr_t attr;
    // 初始化互斥锁属性
    pthread_mutexattr_init(&attr);
    // 设为进程间共享互斥锁
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    // 用属性初始化互斥锁
    pthread_mutex_init(&ptr->lock, &attr);
    
    pid = fork();
    if (pid > 0) {
        // 父进程
        for (int i = 0; i < 5000; i++) {
            pthread_mutex_lock(&ptr->lock);
            printf("Parent pid %d: number = %d\n", getpid(), ++(ptr->number));
            pthread_mutex_unlock(&ptr->lock);
        }
        wait(NULL);
    } else if (pid == 0) {
        // 子进程
        for (int i = 0; i < 5000; i++) {
            pthread_mutex_lock(&ptr->lock);
            printf("Child pid %d: number = %d\n", getpid(), ++(ptr->number));
            pthread_mutex_unlock(&ptr->lock);
        }
        exit(0);
    } else {
        perror("fork call failed");
        exit(0);
    }
    
    close(fd);
    pthread_mutexattr_destroy(&attr);
    pthread_mutex_destroy(&ptr->lock);
    munmap(ptr, sizeof(shared_t));
    return 0;
}

​

文件读写锁

1把写锁,n把读锁,读共享,写互斥,读写互斥

通过修改文件结构体里的文件锁结构体属性来实现上锁和解锁效果。用户需要自定义锁结构体并赋值,而后对文件默认锁结构体进行替换,实现文件上锁效果

​

​

通过查看手册,l_type是锁的类型,有读锁,写锁,未上锁三种状态

l_whence是设置开始加锁的位置(相对于文件整个文件的位置)

l_start是在加锁位置的偏移量

l_len是加锁的长度

l_pid是当前使用锁的进程pid

使用下面这个函数可以设置锁的状态和得到锁的状态,根据第二个参数来判断是得到锁还是上锁。

fcntl(fd,F_GETLK, struct flock * lock) 将文件默认的锁结构体传出到lock变量中

fcntl(fd,F_SETLK, struct flock * newlock)将自定义的newlock替换文件原有的锁结构体,实现上锁效果。F_SETLK是非阻塞上锁,F_SETLKW是阻塞上锁关键字,如果文件锁被占用,则会挂起等待。

下面是对文件进行上锁的demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


int main()
{
    int fd=open("file_test",O_RDWR);
    struct flock old;
    fcntl(fd,F_GETLK,&old);
    //if(old.l_type==F_UNLCK)
    //{
        printf("file_test lock status:F_UNLOCK\n");
        struct flock newlock;
        newlock.l_type=F_WRLCK;
        newlock.l_whence=SEEK_SET;
        newlock.l_start=0;
        newlock.l_len=0;
        fcntl(fd,F_SETLKW,&newlock);
        printf("Process A %d set file_test lock status:F_WRLCK\n",getpid());
        sleep(10);
        newlock.l_type=F_UNLCK;
        fcntl(fd,F_SETLKW,&newlock);
        printf("Process A %d set file_test lock status:F_UNLCK\n",getpid());
    //}
    //else
    //printf("file_test lock status:F_WRLCK\n");
        return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


int main()
{
    int fd=open("file_test",O_RDWR);
        struct flock newlock;
        newlock.l_type=F_WRLCK;
        newlock.l_whence=SEEK_SET;
        newlock.l_start=0;
        newlock.l_len=0;
        fcntl(fd,F_SETLKW,&newlock);
        printf("Process B %d set file_test lock status:F_WRLCK\n",getpid());
        return 0;
}

运行结果:F_SETLKW是阻塞上锁关键字,文件锁被进程A占用,进程B挂起等待

​

​

死锁问题

锁资源有限,但多线程相互请求锁资源,导致线程永久阻塞,这种现象称为死锁。

​

死锁发送的四个必要条件:

请求与保持条件:某个线程在占用一把锁后还要请求新锁,容易引发死锁问题

不可剥夺条件:除了占用资源的线程外,其他人无法解锁资源

互斥条件:某个线程占用锁资源后,其他线程申请则挂起等待

环路等待条件:每个线程都在等待相邻线程手中的资源,这种称为等待环路

下面是产生死锁问题的demo程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


pthread_mutex_t lockA,lockB;
int numA=5;
int numB=6;
void *thread_jobA(void* arg)
{
    pthread_mutex_lock(&lockA);
    printf("numA = %d\n",numA);
    sleep(0);
    pthread_mutex_lock(&lockB);
    printf("numA + numB = %d\n",numA+numB);
    pthread_mutex_unlock(&lockB);
    pthread_mutex_unlock(&lockA);
}

void *thread_jobB(void* arg)
{
    pthread_mutex_lock(&lockB);
    printf("numB = %d\n",numB);
    sleep(0);
    pthread_mutex_lock(&lockA);
    printf("numA + numB = %d\n",numA+numB);
    pthread_mutex_unlock(&lockA);
    pthread_mutex_unlock(&lockB);
}

int main()
{
    pthread_mutex_init(&lockA,NULL);
    pthread_mutex_init(&lockB,NULL);
    pthread_t tid;
    pthread_create(&tid,NULL,thread_jobA,NULL);
    pthread_create(&tid,NULL,thread_jobB,NULL);
    while(1) sleep(1);
        return 0;
}

线程A因线程B占用锁而挂起,线程B因线程A占用锁而挂起。两个线程都没有打印第二行输出。

​

死锁处理

产生死锁后,杀死死锁线程,解除死锁,而后再创建(死锁频繁,创建销毁线程开销较大)

死锁检测

有向图检测

​

可以通过图的遍历算法检测出图中是否出现环路,如果是则已发生死锁,可以通过杀死线程的方式杀死某个节点,解除死锁。

死锁预防

哲学家就餐问题

有五个哲学家,他们围坐在一张圆桌旁,桌上摆放着五个盘子和五根筷子。哲学家们的生活方式是交替进行思考和进餐。为了进餐,哲学家需要两根筷子,因此每个哲学家必须同时从自己左右两侧拿起筷子才能进餐。哲学家放下筷子时,思考开始。

​

死锁:如果每个哲学家拿起自己右边的筷子,并等待左边的筷子,那么他们将永远等待下去,这就形成了死锁。

饥饿:某个哲学家可能一直无法得到两根筷子进餐,导致饿死。

资源竞争:哲学家们同时拿筷子的动作需要同步,以防止出现竞争条件。

​

解锁死锁问题的方法

礼貌策略:当某个哲学家拿起左手的资源,发现无法获取右手的资源,它会放下左手资源,让其他人就餐 

活锁问题:如果哲学家同时频繁触发礼貌机制,导致所有哲学家拿起放下餐具,无法进食,饿死


高权级策略:选中一名哲学家提高权限,此哲学家要进餐时,向相邻哲学家发送通知,让其放下资源

高权策略弊端:由于优先级转换,可能导致多数时间,只有超级哲学家在就餐

银行家算法

银行家算法是一种避免死锁的算法,它通过模拟资源分配,检查在给定资源分配之后,系统是否会进入安全状态。如果会进入安全状态,则分配资源,否则不分配。

银行家算法将资源都抽象为银行资产,每个用户借款,银行进行借款风险评估,如果风险超出阈值,禁止放款

线程控制(条件变量)

条件变量技术实现线程的挂起和唤醒。为线程指定执行条件,按执行条件挂起和唤醒线程。

条件变量类型 pthread_cond_t 线程可以在条件变量上挂起,也可以从中唤醒。

相关函数

pthread_cond_init(pthread_cond_t *cd,NULL) 初始化条件变量

pthread_cond_destroy(pthread_cond_t* cd,NULL) 销毁条件变量

pthread_cond_wait(pthread_cond_t* cd, pthread_mutex_t *lock)等待条件变量的信号。与互斥锁一起使用,实现线程间的同步。在进入等待状态前,自动释放与条件变量关联的互斥锁,以便其他线程能够修改条件并发出信号。

两次执行:
线程第一次执行wait函数,挂起线程的同时解锁互斥锁。
线程被唤醒的同时执行wait,上锁互斥锁

pthread_cond_signal(pthread_cond_t * cd) 唤醒一个挂起在cd中的线程

pthread_cond_broadcast(pthread_cond_t * cd) 唤醒所有挂起在cd中的线程

signal函数的误唤醒,如果系统是多核处理器CPU,signal函数可能唤醒多个CPU,导致误唤醒。

下面这段demo程序实现了条件控制线程交替工作

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

int day_night;
pthread_mutex_t lock;
pthread_cond_t cond;


void * threadA_job(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        while(day_night==0)
        {
            pthread_cond_wait(&cond,&lock);
        }
            printf("Thread A TID 0x%x is working,working state:%d\n",(unsigned int)pthread_self(),day_night);
            --day_night;
            pthread_mutex_unlock(&lock);
            pthread_cond_signal(&cond);
    }
}

void * threadB_job(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        while(day_night)
        {
            pthread_cond_wait(&cond,&lock);
        }
        printf("Thread B TID 0x%x is working,working state:%d\n",(unsigned int)pthread_self(),day_night);
        ++day_night;
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,NULL,threadA_job,NULL);
    pthread_create(&tid,NULL,threadB_job,NULL);
    while(1) sleep(1);
    return 0;
}

​ 

生产者消费者

经典的数据传递或任务传递模型


生产者:任务队列为满挂起,非满则添加数据

生产者生产任务后,通知一个消费者,让它获取任务


消费者:任务队列为空挂起,非空则获取任务执行

消费者获取任务后,唤醒一个生产者


需要两个条件变量

Not_Full Not_Null

遍历时使用环形队列实现

​

下面是实现生产者消费者的demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

pthread_mutex_t lock;
pthread_cond_t Not_Full;
pthread_cond_t Not_Empty;

typedef struct
{
    int id;
    char desc[1024];
}data_t;

typedef struct
{
    data_t * container;
    int Front;
    int Rear;
    int Max;
    int Cur;
}queue_t;

queue_t * Create_queue(int Max)
{
    queue_t * que=(queue_t *)malloc(sizeof(queue_t));
    que->Front=0;
    que->Rear=0;
    que->Max=Max;
    que->Cur=0;
    que->container=(data_t*)malloc(sizeof(data_t)*Max);
    return que;
}

void * Customer_job(void * arg)
{
    pthread_detach(pthread_self());
    queue_t *que=(queue_t*)arg;
    data_t node;
    while(1)
    {
    while(que->Cur==0)
    {
        pthread_cond_wait(&Not_Empty,&lock);
    }
    node=que->container[que->Rear];
    printf("Customer TID 0x%x Get Data Successfully,node id:%d node desc:%s\n",(unsigned int)pthread_self(),node.id,node.desc);
    --(que->Cur);
    que->Rear=(que->Rear+1)%que->Max;
    pthread_mutex_unlock(&lock);
    pthread_cond_signal(&Not_Full);
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t tid[3];
    if(pthread_mutex_init(&lock,NULL)!=0 || pthread_cond_init(&Not_Empty,NULL)!=0 || pthread_cond_init(&Not_Full,NULL)!=0)
    {
        printf("init failed\n");
        exit(0);
    }
    queue_t * que=Create_queue(100);
    data_t node;
    for(int i=0;i<3;++i)
    {
    pthread_create(&tid[i],NULL,Customer_job,(void*)que);
    }

    //生产者循环添加任务
    for(int i=0;i<20;++i)
    {
    node.id=i;
    bzero(node.desc,sizeof(node.desc));
    sprintf(node.desc,"测试数据%d",i);
    pthread_mutex_lock(&lock);
    que->container[que->Front]=node;
    que->Front=(que->Front+1)%que->Max;
    ++que->Cur;
    pthread_mutex_unlock(&lock);
    pthread_cond_signal(&Not_Empty);
    printf("Producer TID 0x%x Add Task Successfully...\n",(unsigned int)pthread_self());
    }
    while(1)
        sleep(1);

        return 0;
}

运行结果:实现了简单的生产者消费者模型,生产者添加任务,消费者执行任务 

​

Linux 多生产者多消费者模型:5个常见死锁场景与规避策略 本文深入探讨Linux多生产者多消费者模型中的5种典型死锁场景,包括唤醒丢失、嵌套锁死锁链、条件变量误用等,并提供工程化规避方案。通过代码示例和性能优化建议,帮助开发者构建高并发系统时有效预防死锁,提升系统稳定性与性能。 阅读详情

相关推荐

从BUCK-BOOST到反激:一个‘拆电感’的视角,轻松理解多路输出交叉调整

本文从BUCK-BOOST电路到反激变换器的演变入手,通过‘拆解电感’的独特视角解析多路输出交叉调整机制。深入探讨了反激电路的能量暂存方式、传递路径变化及电压关系,并结合具体参数对比揭示其特性。针对多路输出中的电压平衡问题,提供了绕组排布优化、元器件选型等实战技巧,帮助工程师有效提升电源系统的稳定性与效率。

weixin_33738578的博客 435

【Linux】用5万字满足你对线程的所有♥幻想♥——【线程概念】【线程安全】【多线程并发】【互斥量】【条件变量】【信号量】【锁的原理】【各种锁】【生产者消费者模型】【读者写者问题】

【Linux】用10万字满足你对线程的所有♥幻想♥——【线程概念】【线程安全】【多线程并发】【互斥量】【条件变量】【信号量】【锁的原理】【各种锁】【生产者消费者模型】【读者写者问题】

2301_79796701的博客 1752

TP4056锂电池充电电路设计原理与工程实践

锂离子电池充电管理是嵌入式电源系统的核心基础技术,其本质是通过恒流恒压(CC/CV)控制策略,匹配电池电化学特性以实现安全、高效、长寿命充放电。TP4056作为典型线性充电IC,以精简外围实现预充、恒流、恒压及自动截止全周期管理,但受限于线性拓扑的热耗散特性,必须协同散热设计、电阻精度选型与PCB布局进行系统级优化。该方案广泛应用于STM32智能小车、便携传感器节点等3.7V单节锂电供电场景,兼顾成本、可靠性与工程可实施性,是入门嵌入式硬件电源设计的关键实践范例。

weixin_36064575的博客 219

【Linux】线程锁&同步互斥&生产消费模型

Linux线程同步、生产消费模型

qq_48460693的博客 1445

OSMDroid(1)——OSM简介,基础例子搭建

1.OSMDroid简介 Osmdroid是一款完全开源的地图基本操作SDK,相比于现在普遍使用的百度地图,高德地图,谷歌地图等最大的好处就是开源。 最近在做一个军方项目,要求完全不能联网,从开发到应用整个阶段都是在局域网进行,相比之下采用了OSMDroid这个SDK作为解决方案。 先来看OSMDroid,便是(OpenStreetMapForAndroid),后面都简称OSM...

robotskly的博客 6177

phaser3适配微信小游戏

phaser3适配微信小游戏

SilenzioX的博客 1803

Linux系统编程 day11 锁 (两天没有更新了,中期完就休息了)

1、尽量保证锁的粒度,越小越好。(访问共享数据前,加锁,访问结束后2、互斥锁,本质是结构体,但是可以看成整数,初值为1。(pthread_mutex_init调用成功)3、加锁: --操作,阻塞线程4、解锁:++操作,唤醒阻塞在锁上的进程try锁:尝试加锁 , 成功-- , 失败:返回错误号(EBUSY),不阻塞。

weixin_71568456的博客 1014

【3D目标检测】MMdetection3d——搭建IS-Fusion环境+nuScenes数据集训练

搭建IS-Fusion环境+nuScenes数据集训练

qq_44703886的博客 2322

【linux】线程 (三)

线程同步

2301_79789645的博客 1018

物理信息神经网络(PINN)文献综述 —— 原理、严格推导与优化训练策略

本章系统阐述物理信息神经网络的理论基础、主要算法改进与训练策略。内容涵盖:PINN 的数学表述与残差构造;损失与梯度的逐步推导;训练困难(梯度消失/爆炸、损失幅度不平衡、频谱偏差、采样效率低等)的定量分析;四类核心解决方案(动态损失加权、梯度归一化与预条件化、自适应采样、分阶段/分域训练)的详细推导与实现要点;以及若干关键算法的伪代码与收敛性形式结论。所有涉及推导的步骤均按顺序展开,给出必要的数学依据与结论性陈述,可直接作为教学或参考章节使用。

u012133341的博客 422

2025.05.28【读书笔记】|如何用SILVA和RFAM数据库高效去除rRNA污染

摘要: 在Ribo-seq等测序数据分析中,清除rRNA污染对结果准确性至关重要。仅使用SILVA数据库可能遗漏5S rRNA污染,建议结合RFAM数据库(特别是RF00001家族)全面清除。操作要点包括:下载两库序列、将RNA碱基U替换为DNA的T、合并序列建立索引,使用bowtie2过滤rRNA reads。注意U/T转换是关键,否则会导致比对失败。该方法能有效提升数据清洁度,为后续分析奠定基础。

978

锁 与 异步

死锁的 根本原因 是 在加锁 与解锁 之间 ,它 又去访问锁,倒是 程序阻塞, 进而永远走不到解锁的步骤。为什么会用到锁呢,我觉得主要就是 为了休眠, 因为获得锁的时候 ,得不到 有休眠的功能。这里 与 僵尸进程 的含义是一样的,就是进程推出之后,资源没有被回收。这里可以 看 completion ,这是个主动睡眠的函数。既然有了 线程 ,那么就有 对共享资源的操作,就是锁了。所有 访问 锁的 ,如果 得不到,都会 阻塞住。2、自旋锁, 是一个 轮询的操作。1 、 线程是轻量级的进程。

weixin_44767571的博客 381

【STK】手把手教你利用STK进行进行光电红外仿真02-STK/EOIR模块属性之一 传感器选项设置

EOIR传感器可以从空间几何属性、光谱属性、光学属性、辐射属性四个方面进行设置,并且可以设置信号传播路径中的大气特性,包括大气模型、气溶胶模型、气象学可见性和大气湿度。在定义EOIR传感器时,需要Sensor——Definition属性页中,将Sensor Type设置为EOIR。单击下面的按钮打开传感器的EOIR配置页,可以对传感器的属性进行配置,此外,还可以在场景中视情况添加相关的中心体。路径辐射损耗影响上述特性的计算,计算的结果考虑了传感器、目标位置的影响,但是还没考虑地球临边背景的影响。

weixin_44184852的博客 3198

【Linux】线程篇Ⅱ:线程的同步与互斥(锁)接口及使用、线程库及线程id的理解

如题

m0_67470729的博客 470

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【Linux】生产者消费者模型:基于阻塞队列和环形队列 | 单例模式线程池

【Linux】生产者消费者模型:基于阻塞队列和环形队列 | 单例模式线程池

旋律 | 话语 | 色彩 | 情景 1268

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【Linux】线程POSIX信号量

本文介绍了信号量的概念及其在多线程编程中的应用。信号量主要用于同步操作,确保无冲突地访问共享资源。POSIX信号量可用于线程间同步,本质是一个计数器,用于预定特定资源。文章详细说明了信号量的基本接口,包括初始化、P操作(申请资源)、V操作(释放资源)和销毁信号量。此外,文章还通过生产者消费者模型的实例,展示了如何利用信号量和互斥锁实现线程间的同步与互斥,特别是在多生产者多消费者场景中,如何通过信号量筛选有效线程,避免死锁,并保证操作的原子性。最后,文章提供了一个基于环形队列的生产者消费者模型的代码实现。

2301_81577798的博客 1万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Linux操作系统之线程生产者消费者

此时子进程已经成为加锁状态,所以在一次上锁,程序变成阻塞状态。那么如何解决这个问题?我们需要保证fork之后,父子进程都有一个清楚的锁状态。计数信号量dfull用来表示有数据的缓冲区的数量,其初值为0。,如果使用的是通一个锁,此时等待5s后,子进程会输出加锁成功。答案先执行p操作,原因当我们先加锁的时候当缓冲区已满的时候,此时生产者p操作阻塞,消费者也上不了锁,程序变为。根据上图我们发现,子进程变为阻塞状态,所以验证了进行fork之后,判断父子线程fork之后,是否使用的同一个锁,首先我们看一段代码。

qq_55703811的博客 623

【Linux系统编程:线程】 线程控制 -- 创建、终止、等待、分离 | 线程互斥与同步 | 互斥量与条件变量 | 生产者消费者模型 | 线程池 | STL/智能指针与线程安全 | 读者写者模型

本文重点:基本上很多操作系统的书上对线程的解释是:线程是进程的一个执行分支,是在进程内部运行的一个执行流。而到具体一款操作系统,我们将从是什么、为什么、怎么办三个角度来解释线程。我们先来谈线程是什么的问题?对于上图,我们当然很熟悉,现在就可以明确了,这里是一张用户级页表,内核页表就不画了,我们都知道可执行程序在磁盘中无非就是代码或数据,更准确点表述,代码也是一种数据,程序一运行,实际就会将其加载到物理内存,因为每个进程都有地址空间和页表,所以可以通过用户页表映射物理内存的方式,来找到磁盘或内存中的数据。当然

DanceBit的博客 2945

【Linux线程】Linux系统多线程(六):<线程同步与互斥>线程同步(上)

这段代码是典型的生产消费模型标准实现,它在底层通过的原子释放特性,完美绕过了“持有锁时挂起”这一死锁禁区。(1)优点: 严谨使用了while判定和RAII思想(虽然是手动初始化,但逻辑闭环)。(2)底层映射: * 原子性:pushpop在锁内。同步性: 靠两个信号通知机制。死锁预防: 靠wait接口内部的“解锁-挂起-加锁”原子序列。

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 771

深入理解Linux线程同步与互斥机制

Linux提供的基本锁机制,通过pthread_mutex_lock和pthread_mutex_unlock保护临界区。实际使用时发现,平台自动处理了环境配置等繁琐步骤,让开发者能专注于核心逻辑的理解和实践。:多线程环境中共享的资源称为临界资源,访问这些资源的代码段就是临界区。:采用固定大小的环形结构和信号量(semaphore)实现更高效的同步,减少锁竞争。:锁的持有时间应尽可能短,只保护必要的临界区,避免影响并发性能。:规范锁的获取顺序,或使用trylock等非阻塞方式。

JetFalcon67的博客 310

Linux:多线程

本文主要介绍了Linux系统中的二级页表机制和线程概念。在二级页表部分,解释了物理内存如何被划分为页框进行管理,以及虚拟地址通过页目录和页表映射到物理地址的过程。线程部分阐述了Linux将线程视为轻量级进程的实现方式,介绍了线程创建、等待等基本操作,并分析了线程相比进程的优缺点。文章还讨论了线程调度成本更低的原因,以及线程与进程在资源分配、调度等方面的区别。最后说明了Linux通过原生线程库pthread为用户提供线程功能。

分享 C/C++、Linux、网络的学习历程与实操心得~ 不追求一蹴而就,只坚持日积月累。未来还将拓展更多技术领域,愿与所有热爱技术的伙伴一起,在探索中成长,在坚持中进步! 1022

Linux--线程同步与互斥

⽣产者和消费者彼此之间不直接通讯,⽽通过阻塞队列来进⾏通讯,所以⽣产者⽣产完数据之后不⽤等待消费者处理,直接扔给阻塞队列,消费者不找⽣产者要数据,⽽是直接从阻塞队列⾥取,阻塞队列就相当于⼀个缓冲区,平衡了⽣产者和消费者的处理能⼒。重⼊:同⼀个函数被不同的执⾏流调⽤,当前⼀个流程还没有执⾏完,就有其他的执⾏流再次进⼊,普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放⼊了元。⼤部分情况,线程使⽤的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量。

m0_74085682的博客 903

信号量与共享内存配合实现生产者-消费者模型

信号量 + 共享内存的典型模式,本质上是“空间换时间”和“同步换安全”的工程权衡。核心环形缓冲区是处理持续数据流的黄金模式。关键:理解并正确实现P/V 操作的顺序,防止死锁。灵魂互斥锁(mutex)保护瞬时状态(指针),资源信号量(emptyfull)控制流程节奏。在实际应用中,一般先用更高层的抽象(如消息队列)验证业务逻辑,仅在性能被证明是瓶颈时,再使用此底层组合。若决定使用,应进行充分的并发测试,并使用ipcsipcrm命令监控和清理系统资源。

qq_32768419的博客 956

【Linux系统与网络编程】13:线程同步

线程同步与锁机制 线程同步是解决多线程竞争共享资源问题的关键技术,主要通过锁机制实现。线程竞争产生于共享资源的非同步访问,临界区是需要保护的代码区域。 主要同步机制: 互斥锁(pthread_mutex_t):确保同一时间只有一个线程访问资源,加锁/解锁操作保证原子性。适用于大部分共享资源保护场景。 读写锁(pthread_rwlock_t):区分读/写操作,允许多读单写,提高读密集型场景的性能。写锁优先级高于读锁。 死锁问题:常见于锁使用不当,如单个线程重复加锁或多个线程循环等待锁。需要合理的锁获取顺序来

https://blog.youkuaiyun.com/m0_74834215 1092

互斥量mutex、锁、条件变量和信号量相关原语(函数)----很全

Linux下c/c++编程,互斥量mutex函数(pthread_mutex_init、pthread_mutex_lock....),读写锁(pthread_mutex_trylock、pthread_rwlock_rdlock.....)、条件变量函数(pthread_cond_init.....)、信号量函数(sem_init....)介绍和使用

caiji0169的博客 1248

Linux C++ 多线程生产者消费者模型:3种同步方案对比与性能实测

本文深入探讨了Linux C++环境下生产者消费者模型的三种同步方案:pthread互斥锁+条件变量、C++11标准库方案和boost无锁队列。通过详细的代码实现和性能测试对比,揭示了各方案在吞吐量、延迟等方面的差异,为开发者提供了基于不同场景的选型指南和优化技巧。

weixin_30444105的博客 408
上一篇: 【Linux】线程Thread
下一篇: 【Linux】Linux下使用套接字进行网络编程
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值