目录
一、什么是线程同步?
二、为什么要使用线程同步?
三、线程同步的五种方式
1、互斥锁
互斥锁 *** 作函数
互斥锁的属性
2、自旋锁
自旋锁 *** 作函数
自旋锁的属性
3、读写锁
读写锁的 *** 作函数
读写锁的特点
读写锁的属性
读写锁的注意事项
4、信号量
信号量 *** 作函数
5、条件变量
条件变量 *** 作函数
注意事项
关于条件变量
总结
结语
前言:文章中的代码是我在vim编辑状态下拷贝过来的,由于我的vim使用插件的原因,导致拷贝过来的代码需要删删改改,如果有错误,还请大佬指出,谢谢!
一、什么是线程同步?即当有一个线程在对内存进行 *** 作时,其他线程都不可以对这个内存地址进行 *** 作,直到该线程完成 *** 作, 其他线程才能对该内存地址进行 *** 作,而其他线程又处于等待状态,实现线程同步的方法有很多。
听完上面的叙述,是不是很匹配互斥类型锁的特色。
二、为什么要使用线程同步?线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行 *** 作,而不是同时进行 *** 作。
三、线程同步的五种方式 1、互斥锁1)加锁和解锁,确保同一时间只有一个线程访问共享资源;
2)访问共享资源之前加锁,访问完成后释放锁;
3)如果某线程持有锁,其他线程形成等待队列;
就比如一个套间里的人使用卫生间,当你上厕所的时候需要关锁,解决完事之后再进行解锁。其他人在外面只能排队等待,等你解决完事解开锁之后,第二个人进去之后又会加锁。
互斥锁 *** 作函数pthread_mutex_t mutex; // 声明锁
int pthread_mutex_init(); // 初始化锁
int pthread_mutex_lock(); // 等待并加锁
int pthread_mutex_trylock(); // 尝试加锁不等待
int pthread_mutex_timedlock(); // 带超时机制的加锁
int pthread_mutex_unlock(); // 解锁
int pthread_mutex_destroy(); // 销毁锁
demo04测试程序代码如下:
#include
#include
#include
#include
#include
int var;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 宏声明并初始化互斥锁互斥锁
void *thmain(void *arg); // 线程主函数
int main(int argc, char* argv[])
{
pthread_t thid1,thid2; // 线程id
// 函数初始化锁
// pthread_mutex_init(&mutex,NULL); // 第一个参数锁的id,第二个锁的属性,下面说
// 创建线程,参数1:线程id,参数2:线程属性,参数3:线程主函数,参数4:线程主函数参数
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待线程退出--第一个参数线程id,第二个参数线程退出状态,这里表示不关心
pthread_join(thid1,NULL); pthread_join(thid2,NULL);
printf("var=%d\n",var);
// 退出程序前销毁锁
pthread_mutex_destroy(&mutex); // 一个参数锁的id
return 0;
}
// 两个线程共用这一个线程主函数
void *thmain(void *arg)
{
for(int ii=0;ii<1000000;ii++)
{
pthread_mutex_lock(&mutex); // 加锁
var++; // 加锁之后操作变量
pthread_mutex_unlock(&mutex); // 解锁
}
}
makefile文件如下:
all: demo04
demo04: demo04.cpp
g++ -g -o demo04 demo04.cpp -lpthread
clean:
rm -rf demo04
编译运行结果如下:
我们程序创建了两个线程,他们使用同一个线程主函数,线程主函数中对全局变量进行100万次加一的 *** 做,所以理想状态下,我们的最终结果应该是200万,所以当结果是200万次的时候就证明了两个线程互相没有干扰,每一次加1都是原子 *** 作,实现了线程同步
上面这段话如果有不理解的地方,可以查看我另外一篇文章《线程安全》
互斥锁的属性PTHREAD_MUTEX_TIMED_NP,这个是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁(递归锁),允许同一个线程对同一个锁成功获取多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,解锁后,请求锁的线程重新竞争。
2、自旋锁自旋锁的功能和互斥锁一样,但是互斥锁在等待锁的过程中会进行休眠,不消耗cpu,而自旋锁在等待锁的过程中会不断地循环检查锁是否可用。
自旋锁和互斥锁不能说谁好谁不好,各有应用场景,因为自旋锁等待的时候会不断检查锁的可用状况,所以适合那种加锁时间极端的场景,互斥锁会休眠,所以适用于那些加锁时间可能会比较长的场景。
另外,自旋锁没有带超时机制的加锁,因为一般自旋锁都是使用在加锁时间极短的场景。
自旋锁 *** 作函数pthread_spinlock_t mutex; // 声明锁
int pthread_spin_init(); // 初始化锁
int pthread_spin_lock(); // 加锁
int pthread_spin_trylock(); // 尝试枷锁
int pthread_spin_unlock(); // 解锁
int pthread_spin_destroy(); // 销毁锁
注意,自旋锁不能再使用宏进行初始化了,只能使用函数,并且自旋锁的初始化锁函数和互斥锁有点不同
互斥锁:pthread_mutex_init(锁的id,锁的属性)
自旋锁:pthread_spin_init(锁的id,锁的共享标志);
自旋锁的属性初始化函数的第二个参数用于设置属性(共享标志)
PTHREAD_PROCESS_SHARED // 共享
PTHREAD_PROCESS_PRIVATE // 私有
实际开发中可能会有多个进程中每个进程又创建了多个线程的场景,如果spin自旋锁的第二个参数,共享标志设置为共享SHARED,就代表多进程的其他进程可以使用这个进程的自旋锁。如果共享标志设置为PRIVATE私有的,就代表只有创建这个自旋锁的进程可以使用该自旋锁。
一般情况下我们设置为私有PTHREAD_PROCCESS_PRIVATE
demo05测试程序源码如下:
#include
#include
#include
#include
#include
int var;
// 注意下面的初始化是spinlock不是spin
pthread_spinlock_t spin; // 自旋锁只能用函数初始化,不可以使用宏
void *thmain(void *arg); // 线程主函数
int main(int argc, char* argv[])
{
// 设置为私有,其他进程不可共享当前进程的自旋锁
pthread_spin_init(&spin,PTHREAD_PROCESS_PRIVATE);
pthread_t thid1,thid2; // 线程id
// 创建线程,参数1:线程id,参数2:线程属性,参数3:线程主函数,参数4:线程主函数参数
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待线程退出--第一个参数线程id,第二个参数线程退出状态,这里表示不关心
pthread_join(thid1,NULL); pthread_join(thid2,NULL);
printf("var=%d\n",var);
// 退出程序前销毁锁
pthread_spin_destroy(&spin); // 一个参数锁的id
return 0;
}
// 两个线程共用这一个线程主函数
void *thmain(void *arg)
{
for(int ii=0;ii<1000000;ii++)
{
pthread_spin_lock(&spin); // 加锁
var++; // 加锁之后操作变量
pthread_spin_unlock(&spin); // 解锁
}
}
makefile不变
运行结果如下:
3、读写锁读写锁允许更高的并发性
三种状态:读模式加锁(读锁)、写模式加锁(写锁)、不加锁
读写锁的 *** 作函数pthread_rwlock_t mutex; // 声明读写锁rw--read-write
int pthread_rwlock_init(); // 初始化读写锁
int pthread_rwlock_destroy(); // 销毁锁
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 声明并初始化读写锁
int pthread_rwlock_rdlock(); // 申请读锁
int pthread_rwlock_tryrdlock(); // 尝试申请读锁,不会产生阻塞
int pthread_rwlock_timedrdlock(); // 带超时机制的申请读锁
int pthread_rwlock_wrlock(); // 申请写锁
int pthread_rwlock_trywrlock(); // 尝试申请写锁,不产生阻塞
int pthread_rwlock_timedwrlock(); // 申请写锁,带有超时机制
int pthread_rwlock_unlock(); // 解锁函数,读锁写锁通用
int pthread_rwlockattr_getpshared(); // 获取读写锁的属性
int pthread_rwlockattr_setpshared(); // 设置读写锁的属性
读写锁的特点只要没有线程持有写锁,任意线程都可以成功申请读锁
只有在所有线程都没有锁的情况下,申请写锁才能成功
读写锁的属性读写锁的属性也是共享标志,分为PTHREAD_PROCESS_PRIVATE(私有)和PTHREAD_PROCESS_SHARED(共享),和自旋锁设置属性的方式一样,参上,不过读写锁有专门设置锁的属性的函数
测试程序demo06.cpp:
#include
#include
#include
#include
#include
#include
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 声明读写锁并初始化
void *thmain(void *arg); // 线程主函数
void handle(int sig); // 信号15的处理函数。
int main(int argc, char* argv[])
{
signal(15,handle); // 当程序接收到15的信号会调用该函数。
pthread_t thid1,thid2,thid3;
// 创建线程,参数1:线程id,参数2:线程属性,参数3:线程主函数,参数4:线程主函数参数
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
sleep(1); // 线程之间sleep 1秒,这样能保证三个线程会一直拥有锁
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
sleep(1);
if(pthread_create(&thid3,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待线程退出--第一个参数线程id,第二个参数线程退出状态,这里表示不关心
pthread_join(thid1,NULL); pthread_join(thid2,NULL); pthread_join(thid3,NULL);
// 退出程序前销毁锁
pthread_rwlock_destroy(&rwlock); // 一个参数锁的id
return 0;
}
// 三个线程共用这一个线程主函数
void *thmain(void *arg)
{
for(int ii=0;ii<100;ii++)
{
printf("线程%lu开始申请读锁...\n",pthread_self());
pthread_rwlock_rdlock(&rwlock); // 第一个线程申请时肯定没有写锁,所以任意线程都能申请读锁
printf("线程%lu申请读锁成功...\n\n",pthread_self());
sleep(5);
pthread_rwlock_unlock(&rwlock); // 解锁
printf("线程%lu已经释放读锁...\n\n",pthread_self());
}
}
void handle(int sig)
{
printf("开始申请写锁...\n");
pthread_rwlock_wrlock(&rwlock); // 加锁
printf("申请写锁成功...\n");
sleep(10);
pthread_rwlock_unlock(&rwlock); // 解锁
printf("写锁已经解除...\n\n");
}
解释一下代码:
上面代码中关于信号处理的方法以后我会讲,这里大家只需要知道15的信号宏名为SIGTERM,也就是我们的killall命令,他就是15的信号。当进程运行时,killall这个进程,就会调用信号处理函数,也就是你使用killall命令杀这个程序的时候,进程收到命令就会开始申请写锁。
这里的代码和之前的略有不同
首先就是创建线程之间会延迟一秒,这里可以先看一下线程主函数中的内容
主函数首先申请了读写锁,因为三个线程中没有线程申请写锁,所以申请一定会成功,那么第一个创建的线程就会持有读锁,然后过了一秒第二个线程又持有了读锁,再过一秒第三个线程也持有了读锁,此时再过两秒之后第一个线程释放了锁,但是还有两个线程持有锁,等线程2释放锁的时候,线程1和线程3还会持有读锁,这就能保证程序运行的时候三个线程会一直持有读锁,所以申请写锁不会成功。
假设我们不延时,三个线程同时(相差时间忽略不计)被创建,最后又同时释放锁,这个时候,因为你一旦发送15的信号,他开始申请写锁就会一直在申请,哪怕你只有几毫秒的时间没有持有锁,写锁也会申请成功。
我们运行程序,发送killall命令,如果申请写锁一直不成功,就说明线程一直持有读锁。
makefile不变
运行如下:
发送15的信号:
结果:
可以看到写锁一直没有申请成功,这足以证明两点:
1、当线程一直持有锁的时候,写锁不会申请成功
2、这个程序是不间断持有读锁的
如果实际开发中这样让程序一直持有读锁,不就造成写入线程饿死的情况了吗?
所以我们实际开发中肯定不能让线程一直持有读锁。
下面的测试程序不是解决写入线程饿死的情况,只是证明下,如果所有线程不持有锁,那么写锁就会申请成功
demo07.cpp:
#include
#include
#include
#include
#include
#include
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 声明读写锁并初始化
void *thmain(void *arg); // 线程主函数
void handle(int sig); // 信号15的处理函数。
int main(int argc, char* argv[])
{
signal(15,handle); // 当程序接收到15的信号会调用该函数。
pthread_t thid1,thid2,thid3;
// 创建线程,参数1:线程id,参数2:线程属性,参数3:线程主函数,参数4:线程主函数参数
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
sleep(1); // 线程之间sleep 1秒,这样能保证三个线程会一直拥有锁
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
sleep(1);
if(pthread_create(&thid3,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待线程退出--第一个参数线程id,第二个参数线程退出状态,这里表示不关心
pthread_join(thid1,NULL); pthread_join(thid2,NULL); pthread_join(thid3,NULL);
// 退出程序前销毁锁
pthread_rwlock_destroy(&rwlock); // 一个参数锁的id
return 0;
}
// 三个线程共用这一个线程主函数
void *thmain(void *arg)
{
for(int ii=0;ii<100;ii++)
{
printf("线程%lu开始申请读锁...\n",pthread_self());
pthread_rwlock_rdlock(&rwlock); // 第一个线程申请时肯定没有写锁,所以任意线程都能申请读锁
printf("线程%lu申请读锁成功...\n\n",pthread_self());
sleep(5);
pthread_rwlock_unlock(&rwlock); // 解锁
printf("线程%lu已经释放读锁...\n\n",pthread_self());
if(ii==3) sleep(8);
}
}
void handle(int sig)
{
printf("开始申请写锁...\n");
pthread_rwlock_wrlock(&rwlock); // 加锁
printf("申请写锁成功...\n");
sleep(10);
pthread_rwlock_unlock(&rwlock); // 解锁
printf("写锁已经解除...\n\n");
}
只加了一行代码,在线程主函数中,当线程第三次释放了锁之后,延时8秒,这样的话有一段时间就能保证全部的线程都释放了锁,我们可以提前申请写锁,他会一直等待,当线程都没有锁的时候,就会立马申请成功,同时写锁申请成功后,在写锁没有释放之前,所有读锁也都不会申请成功。
运行如下:
通过结果得知:
1、线程都没有锁的情况下写锁就会申请成功
2、线程持有写锁的时候所有读锁申请都不会成功
读写锁的注意事项
读写锁适用于对读的次数远大于写的情况
Linux系统优先考虑读锁,这种实现方式可能会导致写入线程饿死的情况,所以一定不能让自己的线程一直都持有读锁,除非你使用读写锁就是为了读取信息不写入。
上面说的是“当其他线程持有锁的时候,申请写锁不会成功”,不是“当其他线程持有读锁的时候,申请写锁不会成功”,可以在程序运行的时候多发几次15的信号申请写锁,自己测试一下吧。
4、信号量信号量的知识,以后会单独拿出来讲,这里只需要知道他的函数怎么用就行了,以及如何用信号量实现互斥锁的功能。
信号量 *** 作函数sem_t *sem; // 声明信号量
int sem_init(); // 初始化信号量
int sem_destroy(); // 销毁信号量
int sem_wait(sem_t *sem); // 信号量的P *** 作
int sem_trywait(sem_t *sem); // 信号量的P *** 作,不阻塞
int sem_timedwait(); // 信号量的P *** 作,带有超时机制
int sem_post(sem_t *sem); // 信号量的V *** 作
int sem_getvalue(); // 获取信号量的值
解释一下,我们这里使用的是二元信号量,也就是信号值只有0和1,0代表不可用,1代表可用,信号量的初始值我们可以通过init函数设置,一般设置为1。当我们使用P *** 作的时候,信号值就会减1,变为0--不可用(类似于加锁的 *** 作),然后当我们使用了V *** 作的时候,信号量就会加1,变为1--可用(类似于解锁的 *** 作)。
以上解释可能不太标准,但大致就是这个意思,关于信号量的知识以后会发布相关文章。
demo08.cpp:
#include
#include
#include
#include
#include
#include
int var;
sem_t sem; // 声明信号量,使用函数初始化
void *thmain(void *arg); // 线程主函数
int main(int argc, char* argv[])
{
pthread_t thid1,thid2; // 线程id
sem_init(&sem,0,1); // 第一个参数,信号量id,第二个先固定填0,第三个信号量初始值
// 创建线程,参数1:线程id,参数2:线程属性,参数3:线程主函数,参数4:线程主函数参数
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待线程退出--第一个参数线程id,第二个参数线程退出状态,这里表示不关心
pthread_join(thid1,NULL); pthread_join(thid2,NULL);
printf("var=%d\n",var);
// 退出程序前销毁锁
sem_destroy(&sem); // 一个参数锁的id
return 0;
}
// 两个线程共用这一个线程主函数
void *thmain(void *arg)
{
for(int ii=0;ii<1000000;ii++)
{
sem_wait(&sem); // 信号量的P操作(加锁)
var++; // 加锁之后操作变量
sem_post(&sem); // 信号量的V操作(解锁)
}
}
makefile文件不变
运行结果如下:
这样就通过二元信号量实现了互斥锁的功能,信号量的知识就先讲这么多。
5、条件变量 条件变量 *** 作函数pthread_cond_t cond; // 生命条件变量
int pthread_cond_init(); // 初始化条件变量
int pthread_cond_destroy(); // 销毁条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 声明并初始化
int pthread_cond_wait(); // 等待被唤醒
int pthread_cond_timedwait(); // 等待被唤醒,带超时机制
int pthread_cond_signal(); // 唤醒一个等待中的线程
int pthread_cond_broadcast(); // 唤醒全部等待中的线程
// 设置条件变量的共享属性,也就是在多个进程的线程之间是否共享条件变量
int pthread_condattr_getpshared(); // 获取共享属性
int pthread_condattr_setpshared(); // 设置共享属性
// 设置条件变量的时钟属性,一般用不到
int pthread_condattr_getclock(); // 获取时钟属性
int pthread_condattr_setclock(); // 设置时钟属性
注意事项首先大家不要被条件变量的名称给误导了,大家就把它当作是一种特殊的锁就行了
另外条件变量必须搭配互斥锁使用,至于为什么以后会讲,大家也可以先猜测猜测是为什么
条件变量的wait()函数会产生阻塞
测试程序demo10.cpp(这里使用激活一个线程的函数)
#include
#include
#include
#include
#include
#include
// 条件变量需要搭配互斥锁使用
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 声明并初始化条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 声明并初始化互斥锁
void* thmain(void *arg); // 线程主函数
void handle(int sig); // 信号处理函数
int main(int argc, char* argv[])
{
signal(15,handle); // 设置信号15的处理函数
pthread_t thid1,thid2,thid3;
// 创建线程
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid3,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待子线程的退出
pthread_join(thid1,NULL); pthread_join(thid2,NULL); pthread_join(thid3,NULL);
// 销毁锁和条件变量
pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex);
return 0;
}
void *thmain(void* arg)
{
while(true)
{
printf("线程%lu开始等待条件信号...\n",pthread_self()); // 获取当前线程号
pthread_cond_wait(&cond,&mutex); // 等待条件信号,注意参数
printf("线程%lu等待条件信号成功...\n\n",pthread_self());
}
}
void handle(int sig)
{
printf("发送条件信号...\n");
pthread_cond_signal(&cond); // 唤醒等待条件信号的一个线程。
/* pthread_cond_broadcast(&cond); // 唤醒等待条件信号的全部线程。 */
}
解释一下代码:
首先创建了三个线程,然后设置了15(killall命令)的信号处理函数。在线程主函数中,每一个线程都是一直在等待条件信号,如果使用一次killall就会调用一次handle()函数,handle函数每调用一次就会激活一个等待中的线程
运行
发送四次15的信号
查看运行结果
我们可以看到,我们发送了四次15的信号,每一次都会调用一次信号处理函数,然而调用一次信号处理函数就会激活一个等待中的线程,但是这里激活线程并不是随机的,而是按照等待的顺序去激活的,仔细观看线程编号就可以发现。
测试程序demo10.cpp(这里使用激活全部线程的函数)
#include
#include
#include
#include
#include
#include
// 条件变量需要搭配互斥锁使用
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 声明并初始化条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 声明并初始化互斥锁
void* thmain(void *arg); // 线程主函数
void handle(int sig); // 信号处理函数
int main(int argc, char* argv[])
{
signal(15,handle); // 设置信号15的处理函数
pthread_t thid1,thid2,thid3;
// 创建线程
if(pthread_create(&thid1,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid2,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
if(pthread_create(&thid3,0,thmain,0)!=0) { printf("create failed.\n"); return -1; }
// 等待子线程的退出
pthread_join(thid1,NULL); pthread_join(thid2,NULL); pthread_join(thid3,NULL);
// 销毁锁和条件变量
pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex);
return 0;
}
void *thmain(void* arg)
{
while(true)
{
printf("线程%lu开始等待条件信号...\n",pthread_self()); // 获取当前线程号
pthread_cond_wait(&cond,&mutex); // 等待条件信号,注意参数
printf("线程%lu等待条件信号成功...\n\n",pthread_self());
}
}
void handle(int sig)
{
printf("发送条件信号...\n");
/* pthread_cond_signal(&cond); // 唤醒等待条件信号的一个线程。*/
pthread_cond_broadcast(&cond); // 唤醒等待条件信号的全部线程。
}
运行
发送信号
结果如下:
可以看到,全部的线程都已经被激活。
关于条件变量关于条件变量,这节课主要讲了条件变量的一些基本函数的 *** 作,以及条件变量必须搭配互斥锁使用,关于条件变量更深入的知识,以后还会继续发文讲解。
总结通过上面的文章,我们大致可以知道,线程同步的方式无非就是加锁。但是,需要注意,锁虽然好用,但是不能说随便加,想加多少加多少,这样的想法是不行的,太多的锁很可能会产生死锁。另外,太多的锁也会降低程序的性能。
所以,在做项目中我们尽量保证不要使用过多的锁,加锁的时间也是越短越好。
此外,关于信号量的知识、条件变量讲解与用途,以后都会继续发文,感谢大家的观看!
结语由于这几天考试的问题,导致这篇文章都是零零散散凑时间写出来的,很多时候写下一个锁的知识的时候就已经忘了上一个锁写的什么了。所以如果文章有错误,请大佬一定指出来,非常感谢大家!如果有看不懂的地方也可以指出来,我会再做更改,尽量通俗易懂。这两天断断续续的写这篇文章脑子难免有些懵懵的,请见谅。另外,大家觉得文章不错的话可以关注一下我,以后看到我发文章,没准会激励起你学习的心,哈哈一起加油吧!!!
凑个封面
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)