一、前言
【第二周】:现在是2019年12月,现大三。希望借这个平台和正在阅读文章的你一起学习和提升。本着分享和记录的目的,我将不定期更新文章,如果文章中有不足之处,还请各位多多指教,如果需要源代码,欢迎在评论区留言邮箱。
二、环境
Linux,C++
三、正文
1、什么环境要用线程池
在上一篇文章中,我们已经可以通过创建多线程的方式实现在定时打印字符串的同时,获取终端的输入内容,那为什么又要引入线程池呢。在我们实际的应用程序中,我们的业务基本是都是可以在短时间内执行完毕的,例如:查询一次数据库,向服务器发送一次HTTP请求,读取一次用户发送的数据等,当然也有长时间才能执行完成的任务,例如:我们在看剧时,视频缓冲就是一个长时间过程,从某云下载一个大文件时就更漫长了。因此,线程池一般是在大并发而任务处理时间短的环境中使用,对于执行时间较长的任务,我们为其创建一个新的线程去处理就行了。
2、为什么要用线程池?
我们知道,相比于进程,线程的系统开销要小的多,虽然线程的创建和销毁所付出的代价不高,但是这一过程仍然需要时间的。如果我们所需要执行的任务是20us,而线程的创建需要10us,也就是说,在整个程序运行的适合,系统在创建和销毁线程上的时间就占了整个程序执行时间的1/3,这无疑是一种效率很低的做法。
例如:
void* func_th1(void* arg)
{
cout << "Hello world!" << endl;
}
int main() {
pthread_t td1;
while (true) {
if (pthread_create(&td1, NULL, func_th1, NULL) != 0) {//创建线程
perror("pthread_create error");
}
pthread_join(td1, NULL);
sleep(1);
}
return 0;
}
在上面的代码中,我们每隔1秒创建一个线程,而我们的任务是打印一个字符串,这时系统创建和销毁线程的时间甚至超过了我们目标业务所执行的时间。其中pthread_join()函数的功能是等待指定线程执行完毕,并回收该线程所占用的系统资源。
3、什么是线程池?
(1)让线程活着
在上面的示例代码中,线程创建后在打印完字符串后就执行完了,“线程死亡”,其所占用的资源将被系统回收。那么有没有什么方法可以让线程活着,在我们需要的时候执行我们的任务呢?对,我们首先想到的是死循环。
所以,我们来编写一个这样的结构。
void* func_th1(void* arg)
{
while (true) {
//我们的任务
}
}
接下来我们让它做点什么呢?在这里,我让它每隔固定时间间隔,执行一次我们的任务函数。
int num;
pthread_mutex_t mutex;//互斥信号量
void myTask() {
cout << "num = " << num << endl;
}
void* func_th1(void* arg)
{
while (true) {
pthread_mutex_lock(&mutex);
myTask();
pthread_mutex_unlock(&mutex);
}
}
int main() {
pthread_t td1;
if (pthread_mutex_init(&mutex, NULL) != 0) {//初始化互斥信号量
perror("pthread_mutex_init error");
return -1;
}
if (pthread_create(&td1, NULL, func_th1, NULL) != 0) {//创建线程
perror("pthread_create error");
return -1;
}
while (true) {
pthread_mutex_lock(&mutex);
sleep(rand()%3+1);
num = rand() % 100;
pthread_mutex_unlock(&mutex);
}
return 0;
}
在上面的代码中,mutex是互斥信号量,如果您对他不了解,在这里,您可以将它理解为一把钥匙,pthread_mutex_lock函数将锁给锁上并把桌上的钥匙拿走了,而pthread_mutex_unlock函数将锁打开并把钥匙放回桌上。如果桌子上没有钥匙,那么程序就会阻塞,知道有人把钥匙放回桌上。我们通过这把钥匙,可以让我们的线程知道什么时候任务是就绪的,可以去执行。
编译运行。
暂时搁置。
TODO://