如何从0开始构建一个线程池【前篇】

一、 前言

【第二周】:现在是2019年12月,现大三。希望借这个平台和正在阅读文章的你一起学习和提升。本着分享和记录的目的,我将不定期更新文章,如果文章中有不足之处,还请各位多多指教,如果需要源代码,欢迎在评论区留言邮箱。

本文主要是引入线程和池线程的概念,以及如何更深入理解线程,如果对线程池的概念已经有了解,请直接跳转【中篇】。

二、环境

Linux,C++

三、正文

1、需求1

任务需求:每隔1S,打印一个字符串。
我们很容易可以想到,直接调用sleep()就可以实现。

int main(){
 while (true) {
  cout << "Hello world!" << endl;
  sleep(1);
 }
 return 0;
}

编译运行,很符合要求。
在这里插入图片描述

2、需求2

任务需求:两个字符串,分别以2S和3S的时间间隔打印一次。
当然我们也很容易可以想到,加上计数器即可实现我们的需求。

int main(){
 int cnt1 = 0, cnt2 = 0;
 while (true) {
  cnt1++;
  cnt2++;
  if (cnt1 == 2) {
   cnt1 = 0;
   cout << "1111 Hello world!" << endl;
  }
  if (cnt2 == 3) {
   cnt2 = 0;
   cout << "2222 Hello world!" << endl;
  }
  sleep(1);
 }
 return 0;
}

编译运行,可以实现我们的需求。
在这里插入图片描述

3、需求3

任务需求:2个字符串,分别以1S,3S的时间间隔打印一次,同时打印终端输入的字符串
这个时候,如果只用一个进程,就很难实现我们的需求了。这个时候,我们就需要使用多线程技术了。

4、线程真的能提高效率吗

如果您对线程的理解停留在 “多个任务同一时刻一起运行”,那么以下的描述可能让您对【线程】有一些新的认识。

(1)操作系统级:

首先,在操作系统中,线程是操作系统调度的基本单位,同一个进程的不同线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等。
在操作系统级别,TCB即线程控制块中存储的信息有:
(1)线程状态。
(2)当线程不运行时,被保存的现场资源。
(3)一组执行堆栈。
(4)存放每个线程的局部变量主存区。
(5)访问同一个进程中的主存和其它资源。

(2)CPU硬件级别:

首先,在单核心CPU系统中,多线程技术并不能提升CPU的利用率,因为单核CPU在同一时刻只能执行某一段指令,也就是我们的代码,所以在这种情况下,我们的线程并不是在同一时刻同时被执行的,在当前程序的执行周期时,任意时刻只有一个线程占用CPU。

那么,在单核CPU系统中,还有必要用多线程吗?

答案是肯定的。
我们知道我们的常用的操作系统大部分基于时间片进行调度的,在这里为了方面理解和描述,我做一个简单的假设,我们的系统中有10个单线程程序,系统的时间片是1ms。也就是说,每一个程序在1S内可以占用CPU 1/10秒 也就是100ms。这个时候我们执行一个我们自己写的单线程程序,这时系统中有11个程序,每一个程序在1S内可以占用CPU 1/11秒 ≈ 99ms。
此时,我们的程序和其它程序对于CPU的占有率是一样的,这时我们如果将程序更改为10个线程运行的多线程程序呢?我们的程序就可以在1S内占用CPU 1/20 * 10 = 500ms。也就是说我们的程序可以更频繁地被CPU调度,对于一个计算密集型的程序,这无疑可以让我们的程序更快地执行完。
所以,我们可以得出一个结论,在单核CPU系统中,适当引入多线程也可以加快程序的执行速度,其本质是和其它程序争夺CPU的占用权。

那么在多核心CPU系统中呢?
多核心CPU系统,也就意味着多个处理器可以在同一时刻完成不同的计算。这时,多线程技术就可以很大程度地提高我们CPU的利用率以及程序的执行速度。
但是,如果我们有这样两个线程。
线程1:计算a = 100+50+40+30;a = a+87;
线程2:计算b = 8+1;b = b+a;
在程序开始执行时,线程1和线程2都可以同时执行,但是当线程2计算完8+1时,如果线程1还没有计算出a的结果,那么线程2就要等待,知道线程1计算完,因为线程2接下来的计算需要依赖a的计算结果。
因此,在编写多线程程序时,我们需要尽量程序间的耦合度,让线程处理的任务相对独立。否则在高耦合度的程序中,大量使用线程,效果可能适得其反。

5、如何创建线程

在Linux系统中,我们需要调用线程库中的库函数来创建线程,引入头文件<pthread.h>。
特别注意,在编译多线程程序时,我们需要指定链接库参数: -lpthread.
创建线程需要使用的函数:

/* Create a new thread, starting with execution of START-ROUTINE
   getting passed ARG.  Creation attributed come from ATTR.  The new
   handle is stored in *NEWTHREAD.  */
extern int pthread_create (pthread_t *__restrict __newthread,
      const pthread_attr_t *__restrict __attr,
      void *(*__start_routine) (void *),
      void *__restrict __arg) __THROWNL __nonnull ((1, 3))

参数:
参数1:为指向线程标识符的指针。
参数2:用来设置线程属性,一般指定为NULL。
参数3:是线程运行函数的起始地址,即线程执行的函数的函数名。
参数4:传入的参数。
返回值:
成功返回0,失败返回-1,并设置错误码。

有了多线程,我们的需求3就可以很好地解决了

#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>

using namespace std;
void* func_th1(void* arg)
{
 while (true) {
  cout << "111 Hello world!" << endl;
  sleep(1);
 }
}
void* func_th2(void* arg)
{
 while (true) {
 cout << "222 Hello world!" << endl;
 sleep(3);
 }
}
int main() {
 pthread_t td1, td2;
 if (pthread_create(&td1, NULL, func_th1, NULL) != 0) {//创建线程
  perror("pthread_create error");
 }
 if (pthread_create(&td2, NULL, func_th2, NULL) != 0) {//创建线程
  perror("pthread_create error");
 }
 while (true) {//监听控制台输入
  char buf[512];
  cin >> buf;
  cout << "OUT:" << buf << endl;
 }
 return 0;
}

编译运行,很容易就可以实现我们想要的效果。
在这里插入图片描述
有了线程,我们就可以开始构建我们的线程池了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值