• 线程同步与互斥(线程安全)


    同步 : 临界资源的合理访问
    互斥 : 临界资源同一时间唯一访问
    互斥变量不一定要是全局变量, 只要多个线程都能访问到就行了

    互斥锁
    #include <pthread.h>

    pthread_mutex_t mutex; //创建一把互斥锁

    int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁互斥锁

    int pthread_mutex_init(pthread_mutex_t *restrict mutex,//初始化互斥锁
    const pthread_mutexattr_t *restrict attr);
    restrict : 可加可不加, 加了restrict那么attr指向的区域不能被其他同类型指针访问

    //锁定互斥锁, 并检测当前互斥锁是否锁定, 如果当前没有锁定, 就锁定并返回0,否则阻塞等待
    int pthread_mutex_lock(pthread_mutex_t *mutex);

    //尝试锁定, 如果当前没有锁定, 就锁定并返回0, 否则返回错误号
    int pthread_mutex_trylock(pthread_mutex_t *mutex);

    //解锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex);

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    互斥锁特点:
    1.多个线程访问共享数据的时候是串行的
    使用互斥锁的缺点:
    1.效率低
    互斥锁的使用步骤:
    1.创建互斥锁: pthread_mutex_t mutex;
    2.初始化互斥锁: pthread_mutex_init(&mutex, NULL) : --mutex = 1
    3.寻找共享资源, 在操作共享资源前加锁
    pthred_mutex_lock(&mutex) : --mutex = 0
    4.解锁
    pthread_mutex_unlock(&mutex): --mutex = 1
    临界区:
    在lock和unlock之间的被锁住的区域叫做临界区, 临界区越大代码执行效率越差
    实际开发中临界区应越小越好
    加锁的原因:
    1.模拟原子操作
    2.线程同步
    必要条件:
    1.互斥条件, (我操作时别人不能操作)
    2.不可剥夺, (我加的锁别人不能解锁)
    3.请求与保持条件, (拿着手里的, 请求其他的, 其他的请求不到, 也不放开手里的)
    4.环路等待条件
    产生的场景:
    1.加锁解锁顺序不同
    预防死锁: 破坏必要条件
    避免死锁: 死锁检测算法, 银行家算法
    死锁处理:
    1.自己锁自己
    2.资源数大于锁数, (开发应用中,应该有几个共享资源就对应有几把锁)
    3. 线程1对共享资源A加锁成功
    线程2对共享资源B加锁成功

    线程1访问共享资源B, 对B加锁----线程1阻塞在B锁上
    线程2访问共享资源A, 对A加锁----线程2阻塞在A锁上

    如何解决:
    ---让线程按照一定的顺序去访问共享资源
    ---在访问其他锁的时候, 需要先将自己的锁解开
    ---trylock方式加锁
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    读写锁
    1.读写锁是一把锁
    pthread_rwlock_t lock;
    2.读写锁的类型:
    读锁 : 对内存做读操作
    写锁 : 对内存做写操作
    3.读写锁的特性
    1.线程A加读锁成功, 又来了三个线程, 做读操作, 可以加锁成功
    ---读共享, 可以并行处理
    2.线程A加写锁成功, 又来了三个线程, 做读操作, 三个线程阻塞
    ---写独占
    3.线程A加读锁成功, 又来了B线程加写锁阻塞, 又来了C线程加读锁阻塞
    ---读写不能同时进行
    ---写的优先级高
    4.读写锁场景练习
    线程A加写锁成功, 线程B请求读锁
    ---线程B阻塞
    线程A持有读锁, 线程B请求写锁
    ---线程B阻塞
    线程A拥有读锁, 线程B请求读锁
    ---线程B请求读锁成功
    线程A拥有读锁, 然后线程B请求写锁, 然后线程C请求读锁
    ---线程B和C都阻塞(写的优先级高, C的优先级与B比较后, 阻塞)
    ---A解锁, B成功C阻塞
    ---B解锁, C成功
    线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁
    ---B和C阻塞
    ---A解锁, C成功B阻塞
    ---C解锁, B成功
    5.读写锁的适用场景
    互斥锁 : 读写串行
    读写锁 :
    读 : 并行
    写 : 串行
    程序中的读操作远多于写操作时,适用读写锁
    6.主要操作函数
    #include <pthread.h>

    //初始化读写锁
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
    const pthread_rwlockattr_t *restrict attr);
    //销毁读写锁
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    //加读锁
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    //尝试加读锁
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    //加写锁
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    //尝试加写锁
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    //解锁
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    实现同步的方法 : 临界资源访问合理性—生产出来才能使用 没有资源则等待, 生产资源后唤醒等待
    条件变量
    1. 条件变量不是锁,它是能阻塞线程的函数
    条件变量+互斥锁实现线程同步
    互斥锁 : 保护一块共享数据
    条件变量: 引起阻塞
    ---生产者和消费者模型
    2.条件变量的两个动作
    条件不满足 : 阻塞线程
    当条件满足 : 通知阻塞的线程开始工作
    3.条件变量的类型 : pthread_cond_t;
    4.主要函数:
    初始化一个条件变量
    int pthread_cond_init(pthread_cond_t *restrict cond,
    const pthread_condattr_t *restrict attr);
    销毁一个条件变量
    int pthread_cond_destroy(pthread_cond_t *cond);
    阻塞等待一个条件变量
    int pthread_cond_wait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex);
    限时等待一个条件变量
    int pthread_cond_timedwait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex,
    const struct timespec *restrict abstime);
    唤醒至少一个阻塞在条件变量上的线程
    int pthread_cond_signal(pthread_cond_t *cond);
    唤醒全部阻塞在条件变量上的线程
    int pthread_cond_broadcast(pthread_cond_t *cond);

    不是什么时候都能阻塞线程
    情景 :
    链表 Node *head = NULL;
    while(head == NULL)
    {
    //我们想让代码在这个位置阻塞
    //等待链表中有了结点之后, 再继续往下走
    //使用了条件变量, 阻塞线程
    }
    //链表不为空的处理代码

    生产者和消费者模型:一个场所,两种角色,三种关系
    功能:解耦和,支持忙闲不均,支持并发
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    信号量(信号灯)posix标准下
    1.头文件--semaphore.h
    2.信号量类型
    sem_t sem;
    加强版的互斥锁
    3.主要函数
    初始化信号量
    #include <semaphore.h>

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

    Link with -pthread.
    0---线程同步
    1---进程同步
    value---最多有几个线程操作共享数据
    销毁信号量
    int sem_destroy(sem_t *sem);
    加锁
    int sem_wait(sem_t *sem);
    调用一次相当于对sem做了--操作
    如果sem值为0, 线程会阻塞
    尝试加锁
    int sem_trywait(sem_t *sem);
    sem==0, 加锁失败, 不阻塞, 直接返回
    限时尝试加锁
    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
    解锁
    int sem_post(sem_t *sem);
    对sem做了++操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    信号量与条件变量的区别:信号量具有资源计数功能,临界资源是否能够操作通过自身技术判断
    条件变量需搭配互斥锁一起使用
    信号量还能实现互斥,计数仅为0/1
    ---------------------

  • 相关阅读:
    【转】win7“您可能没有权限使用网络资源”的解决办法
    windows下顽固软件卸载不了的解决方法
    【转】Windows Server 2008修改远程桌面连接数
    winserver2008,运行可执行文件,提示 激活上下文生成失败。 找不到从属程序集 Microsoft.VC90.DebugCRT,processorArchitecture="x86"
    保障视频4G传输的流畅性,海康威视摄像头相关设置
    【转】win7如何设置共享目录,并且访问不需要输入用户名和密码。
    CentOS7.1配置远程桌面
    C++遍历目录,并把目录里超过7天的文件删除(跨平台windows&linux)
    hibernate(二)一级缓存和三种状态解析
    Android进程间的通信之AIDL
  • 原文地址:https://www.cnblogs.com/hyhy904/p/10954473.html
Copyright © 2020-2023  润新知