线程间通信 | 避免资源竞争、实现同步通信

一、实现方式

1.一个进程空间内部的所有线程共享数据段和堆区,所以全局变量、静态变量,堆区空间都是共享的,可利用这些空间通信;

2.多线程操作全局变量空间时会引入资源竞争;

3.多线程可通过加互斥锁避免资源竞争问题;

4.线程间通信最简单的方法:全局变量+锁

二、原子操作

不会被CPU任务调度打断的一次最小的操作

三、互斥锁

避免多线程资源竞争,配合资源使用,使用资源前加锁,使用资源结束后解锁;

加锁后,无法再次加锁,必须等到解锁后才能继续加锁。

加锁不能阻止CPU任务调度,只是避免资源竞争问题 ,后一个线程想要执行,需等到前一个线程解锁之后才有可能执行。

四、临界代码与临界区

加锁解锁中间的代码称为临界代码或临界区;

临界代码和临界区不可能同时被CPU执行。

五、互斥锁函数接口

1.pthread_mutex_init

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

功能:互斥锁初始化(在线程创建前初始化)

参数:

mutex:互斥锁空间首地址;

attr:互斥锁属性默认传NULL

返回值:

成功返回0;

失败返回非0

2.pthread_mutex_destory

int pthread_mutex_destory(pthread_mutex_t *restrict mutex);

功能:互斥锁销毁(在线程结束后销毁)

参数:

mutex:互斥锁空间首地址;

返回值:

成功返回0;

失败返回非0

3.pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:互斥锁加锁

参数:

mutex:互斥锁空间首地址;

返回值:

成功返回0;

失败返回非0

4.pthread_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:互斥锁解锁

参数:

mutex:互斥锁空间首地址;

返回值:

成功返回0;

失败返回非0

六、死锁

多任务通信过程中由于加锁导致多个任务均无法向下执行的状态

1.死锁产生的四个必要条件
  • 互斥条件
  • 不可剥夺条件
  • 请求保持
  • 循环等待
2.解决死锁
  • 用pthread_mutex_trylock替代pthread_mutex_lock,如果无法加锁成功完成异常处理流程,防止程序卡死
  • 加锁顺序保持一致

示例:

cpp 复制代码
#include<stdio.h>
#include<pthread.h>

int Num = 100;
int TmpValue1 = 0;
int TmpValue2 = 0;
pthread_mutex_t lock;

void *ThreadFun1(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        TmpValue1 = Num;
        TmpValue2 = Num;
        Num++;
        pthread_mutex_unlock(&lock);
    }

    return NULL;
}

void *ThreadFun2(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        if(TmpValue1 != TmpValue2)
        {
            printf("TmpValue1 = %d TmpValue2 = %d\n", TmpValue1, TmpValue2);
        }
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main(void)
{
    pthread_t tid1;
    pthread_t tid2;

    pthread_mutex_init(&lock, NULL);

    pthread_create(&tid1, NULL, ThreadFun1, NULL);
    pthread_create(&tid2, NULL, ThreadFun2, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    pthread_mutex_destroy(&lock);

    return 0;
}

七、信号量

1.信号量可实现多线程间的同步,让多个任务具有先后顺序关系

同步:具有严格的先后执行的逻辑关系;

异步:代码执行流程没有任何关联性。

2.信号量是一个资源,资源可以初始化、销毁、申请和释放

如果资源数>0,则申请资源是让资源数-1;

如果资源数为0,申请资源时则会阻塞等待,等待有人释放资源,释放资源不会阻塞,会让资源数+1.

八、信息量的函数接口

1.sem_init

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:对信号量初始化;

参数:

sem:信号量空间首地址;

pshared:信号量作用域;

value:信号量的初始值

返回值:

成功返回0;

失败返回非0

2.sem_destory

int sem_destory(sem_t *sem);

功能:销毁信号量;

参数:

sem:信号量空间首地址;

返回值:

成功返回0;

失败返回非0

3.sem_wait

int sem_wait(sem_t *sem);

功能:申请资源,让资源数-1,如果资源数为0,则阻塞等待

参数:

sem:信号量空间首地址;

返回值:

成功返回0;

失败返回非0

4.sem_post

int sem_post(sem_t *sem);

功能:释放资源,让资源数+1

参数:

sem:信号量空间首地址;

返回值:

成功返回0;

失败返回非0

示例:创建三个线程,线程1循环打印A,线程2循环打印B,线程3循环打印C,按顺序打印出ABC

cpp 复制代码
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

sem_t sem1_w;
sem_t sem2_w;
sem_t sem3_w;

void *ThreadFun1(void *arg)
{
    while(1)
    {
        sem_wait(&sem1_w);
        printf("A");
        sem_post(&sem2_w);
    }

    return NULL;
}

void *ThreadFun2(void *arg)
{
    while(1)
    {
        sem_wait(&sem2_w);
        printf("B");
        sem_post(&sem3_w);
    }

    return NULL;
}

void *ThreadFun3(void *arg)
{
    while(1)
    {
        sem_wait(&sem3_w);
        printf("C\n");
        sem_post(&sem1_w);
    }

    return NULL;
}

int main(void)
{
    pthread_t tid1;
    pthread_t tid2;
    pthread_t tid3;

    sem_init(&sem1_w, 0, 1);
    sem_init(&sem2_w, 0, 0);
    sem_init(&sem3_w, 0, 0);

    pthread_create(&tid1, NULL, ThreadFun1, NULL);
    pthread_create(&tid2, NULL, ThreadFun2, NULL);
    pthread_create(&tid3, NULL, ThreadFun3, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    sem_destroy(&sem1_w);
    sem_destroy(&sem2_w);
    sem_destroy(&sem3_w);

    return 0;
}
相关推荐
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
未济4 天前
linux 配置环境变量
linux
傲世仙尊4 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
伞伞悦读4 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
Seoyoneh4 天前
呼叫中心系统上云还是本地部署?架构、成本与运维全维度技术对比
人工智能·信息与通信·通信
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++
_艾伦 耶格尔.4 天前
进程间通信
linux
Liuqy-054 天前
Linux IO编程——静态库、动态库
linux
霍霍的袁4 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
彧azz4 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试