线程的知识点太多,太重要,所以分成三部分进行总结学习
线程安全
多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。
多个线程对临界资源进行竞争操作时若不会造成数据二义性时则线程安全;否则,此时就是不安全的
如何实现线程安全
常见的线程安全的情况
每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的
类或者接口对于线程来说都是原子操作
多个线程之间的切换不会导致该接口的执行结果存在二义性
常见的线程不安全的情况
不保护共享变量的函数
函数状态随着被调用,状态发生变化的函数
返回指向静态变量指针的函数
调用线程不安全函数的函数
在网上调研过程中看到一个总结:减少对临界资源的依赖,尽量避免访问全局变量,静态变量或其它共享资源,如果必须要使用共享资源,所有使用到的地方必须要进行互斥锁 (Mutex) 保护
所以当对临界资源使用时,尽量在必须的地方使用锁的保护
对临界资源又有两种访问,分别是同步访问和互斥访问
同步:临界资源的合理访问
异步:临界资源同一时间的唯一访问
互斥锁
互斥锁的操作就是1/0的操作
一个0或者1的计数器。1可以表示加锁,加锁就是计数-1;操作完毕之后要解锁,解锁就是计数+1;
0表示不可以加锁,不能加锁则等待
//互斥锁的接口
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//函数应销毁mutex引用的mutex对象
//注意!!!
//销毁已解锁的已初始化互斥体应是安全的。试图销毁锁定的互斥体会导致未定义的行为。
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
//mutex:互斥锁变量
//attr:属性,通常为NULL
//应使用attr指定的属性初始化mutex引用的mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//宏PTHREAD_MUTEX_INITIALIZER来静态的初始化锁
//互斥锁变量不一定非要全局变量–只要保证要互斥的线程都能访问到就行
int pthread_mutex_lock(pthread_mutex_t *mutex);
//锁定mutex引用的mutex对象。如果互斥体已被锁定,则调用线程应阻塞,直到互斥体可用。此操作将返回互斥对象引用的互斥对象处于锁定状态,调用线程作为其所有者。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//函数应等同于pthread_mutex_lock(),但如果mutex引用的mutex对象当前被锁定(由任何线程,包括当前线程),则调用应立即返回。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//函数应释放mutex引用的mutex对象。互斥体的释放方式取决于互斥体的type属性。如果在调用pthread_mutex_unlock()时,mutex引用的mutex对象上有线程被阻塞,导致mutex可用,调度策略应确定哪个线程应获取mutex。
互斥锁的操作步骤
定义互斥锁变量
初始化互斥锁变量
加锁
解锁
销毁互斥锁
通过一个互斥锁Demo来感受一下锁的使用
//模拟黄牛抢票,100张票,共有四个黄牛在抢票
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
int ticket = 100;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//定义初始化锁
void* thr_start(void* arg){
while(1){
pthread_mutex_lock(&mutex);
if(ticket > 0){
usleep(1000);
printf("yellow bull : %d----get ticket : %d\n",(int)arg,ticket);
ticket--;
}else{
pthread_mutex_unlock(&mutex);