一,进程线程间的互斥相关背景概念
-
临界资源:多线程执行流共享的资源就叫做临界资源
-
临界区:每个线程内部,访问临界自娱的代码,就叫做临界区
- 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
-
原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成
二,互斥量mute
-
多个线程并发的操作共享变量,会带来一些问题。
-
大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个 线程,其他线程无法获得这种变量。
-
但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之 间的交互。
四,互斥锁常用的函数
pthread_mutex_init函数
作用:初始化一个锁
原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t
*restrict attr);
参数:
mutex
:要初始化的互斥量
attr
:设置所的相关属性,如果设为空则代表使用默认的属性
返回值:如果成功,pthread_mutex_destroy()和pthread_mutex_init()函数将返回零;否则,将返回一个错误编号给indi‐找出错误
pthread_mutex_destroy函数
作用:销毁一个锁
原型:
int pthread_mutex_destroy(
pthread_mutex_t *restrict_mutex);
参数:
restrict_mutex要销毁的锁
返回值:
如果成功,pthread_mutex_destroy()和pthread_mutex_init()函数将返回零;否则,将返回一个错误编号给indi‐找出错误
pthread_mutex_lock函数
作用:关锁
原型:
int pthread_mutex_lock(pthread_mutex_t *restrict_mutex)
参数:
restrict_mutex:要关的锁
返回值:如果成功,pthread_mutex_lock()和pthread_mutex_unlock()函数将返回零;否则,将返回一个错误编号来指示这个错误。
pthread_mutex_unlock函数
作用:开锁
原型:
int pthread_mutex_unlock(pthread_mutex_t *restrict_mutex)
参数:
restrict_mutex:要开的锁
返回值:如果成功,pthread_mutex_lock()和pthread_mutex_unlock()函数将返回零;否则,将返回一个错误编号来指示这个错误。
以上函数在具体操作中的使用如下:
#include <iostream>
#include <pthread.h>
using namespace std;
int count = 100000;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
class my_mutex
{
public:
const char* arr;
pthread_mutex_t* _mutex;
public:
my_mutex(const char* arg,pthread_mutex_t* mutex)
:arr(arg),
_mutex(mutex)
{}
~my_mutex()
{
arr=nullptr;
}
};
void *fun(void *arg)
{
my_mutex* a=(my_mutex*)arg;
while (true)
{
pthread_mutex_lock((a->_mutex));
if (count > 0)
{
cout << a->arr << " " << count << " " << pthread_self() << endl;
count--;
pthread_mutex_unlock((a->_mutex));
}
else
{
pthread_mutex_unlock((a->_mutex));
break;
}
}
}
int main()
{
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,nullptr);
pthread_t thread1; my_mutex mutex1("线程一",&mutex);
pthread_t thread2; my_mutex mutex2("线程二",&mutex);
pthread_t thread3; my_mutex mutex3("线程三",&mutex);
pthread_create(&thread1, nullptr, fun, (void *)&mutex1);
pthread_create(&thread2, nullptr, fun, (void *)&mutex2);
pthread_create(&thread3, nullptr, fun, (void *)&mutex3);
pthread_join(thread1, nullptr);
pthread_join(thread2, nullptr);
pthread_join(thread3, nullptr);
pthread_mutex_destroy(&mutex);
cout << "从线程全部退出" << endl;
return 0;
}
最后一条语句打印count为1,代表锁的使用成功
五,可重入VS线程安全
-
线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作, 并且没有锁保护的情况下,会出现该问题。
-
重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们 称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任题,则该函数被称为可重 入函数,否则,是不可重入函数
六,常见的线程不安全的情况
-
不保护共享变量的函数
-
函数状态随着被调用,状态发生变化的函数返回指向静态变量指针的函数调用线程不安全函数的函数
七,
常见的线程安全的情况
-
每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的
-
类或者接口对于线程来说都是原子操作多个线程之间的切换不会导致该接口的执行结果存在二义性
###
死锁四个必要条件
互斥条件:一个资源每次只能被一个执行流使用
请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件
:
一个执行流已获得的资源,在末使用完之前,不能强行剥夺
循环等待条件
:
若干执行流之间形成一种头尾相接的循环等待资源的关系
###
避免死锁
破坏死锁的四个必要条件
加锁顺序一致
避免锁未释放的场景
资源一次性分配