1 线程与进程

1.1 线程和进程对比

进程 线程
程序的一次动态执行过程 一个进程内部的控制序列
一个进程只能对应一个程序, 一个程序可以有多个进程实例 一切进程至少都有一个线程
资源竞争的基本单位 程序执行的最小单位
进程间不共享数据 线程共享进程数据,也拥有自己的结构数据
fork新进程后,有自己的变量和PID,完全独立于父进程 创建新线程后,有自己的堆栈,但与父进程共享全局变量,文件描述符,信号处理和当前工作目录状态

imgclip14.png

1.2 线程优点

  • 创建新线程代价比创建新进程小
  • 线程间切换代价小
  • 线程占用的资源少
  • 线程能充分利用多处理器的并行数量。计算密集型应用,将计算分解为多个线程实现,可在多处理器系统运行。
  • 在等待慢速IO操作时,程序可执行其他任务。IO密集型应用,为了提高性能,将IO操作重叠。线程可以同时等待不同的IO操作。

1.3 线程的缺点

imgclip1.png

2 线程模型

2.1 N:1用户线程模型

imgclip2.png
imgclip3.png

2.2 1:1核心线程模型

imgclip4.png

2.3 N:M混合线程模型

该模型提供了两级控制,将用户线程映射为系统的可调度体(称为LWP轻量级进程)以实现并行,LWP再一一映射到核心线程。
POSIX线程就是使用这种模型。

imgclip5.png
imgclip6.png

3 POSIX线程

3.1 POSIX线程API

头文件:

  1. #include <pthread.h>

同时编译时需要加上链接线程库的命令:-lpthread

3.1.1 pthread_create

原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:创建一个新线程
返回值:成功返回0, 失败返回错误码
参数:

  • thread:返回的线程ID
  • attr:设置线程属性,NULL代表使用默认属性
  • start_routine:函数指针,即线程启动后要执行的函数
  • arg:函数指针需要的参数

注意:pthread相关的函数出错时不会设置全局变量errno,而是通过返回值。

3.1.2 pthread_join

原型:int pthread_join(pthread_t thread, void **retval);
功能:等待创建的新线程结束,类似进程的waitpid函数
返回值:成功返回0, 失败返回错误码
参数:

  • retval:线程的exit状态,return的返回值或pthread_exit的retVal,可以置为NULL

3.1.3 pthread_exit

原型:void pthread_exit(void *retval);
功能:终止线程,类似进程的exit函数。也可以调用return
返回值:无返回值,跟进程一样,线程结束无法返回到它的调用者
参数:

  • retval:返回状态,不能指向一个局部变量

3.1.4 pthread_self

原型:pthread_t pthread_self(void);
功能: 返回本线程的id
返回值: 成功返回线程id

3.1.5 pthread_cancel

原型:int pthread_cancel(pthread_t thread);
功能:取消一个执行中的新线程
返回值:成功返回0, 失败返回错误码
参数:

  • thread:线程id

3.1.6 pthread_detach

原型:int pthread_detachl(pthread_t thread);
功能:将一个线程与调用者分离
返回值:成功返回0, 失败返回错误码
参数:

  • thread:线程id

3.2 线程属性设置

imgclip7.png
imgclip8.png
imgclip9.png
imgclip10.png

3.3 线程私有数据

线程除了共享其他线程和主线程的信息之外,还有128个key-value的结构(PTHREAD_KEY_MAX决定每个系统支持的个数)可以保存本线程私有的数据,这些数据不被其他线程访问,但是可以被本线程的所有函数访问,类似于本线程中的全局变量
结构如下:

imgclip11.png

如下函数用于设置key-value数据段的数据:

imgclip12.png

3.4 代码示例

3.4.1 线程创建

  1. #include "common.h"
  2. #include <pthread.h>
  3. void* thread_function(void* arg)
  4. {
  5. for (int i=0; i<20; i++)
  6. {
  7. printf("slave thread said B\n");
  8. fflush(stdout);
  9. usleep(20);
  10. }
  11. sleep(3);
  12. return 0;
  13. }
  14. int main()
  15. {
  16. pthread_t tid;
  17. int ret = pthread_create(&tid, NULL, thread_function, NULL);
  18. if (ret != 0)
  19. {
  20. //错误处理
  21. printf("pthread_create failed, error message: %s\r\n", strerror(ret));
  22. exit(EXIT_FAILURE);
  23. }
  24. for (int i=0; i<20; i++)
  25. {
  26. printf("master thread said A\n");
  27. fflush(stdout);
  28. usleep(20);
  29. }
  30. //主线程等待新创建的线程结束,不然会直接终止
  31. ret = pthread_join(tid, NULL);
  32. if (ret != 0)
  33. {
  34. //错误处理
  35. printf("pthread_join failed, error message: %s\r\n", strerror(ret));
  36. exit(EXIT_FAILURE);
  37. }
  38. return 0;
  39. }

3.4.2 私有数据区操作

  1. #include "common.h"
  2. #include <pthread.h>
  3. struct tsd
  4. {
  5. pthread_t tid;
  6. char* str;
  7. };
  8. pthread_key_t key;
  9. void destroy_func(void* value)
  10. {
  11. //删除key时调用该函数
  12. printf("destroy the key here\n");
  13. free(value); //释放malloc的内存,这里value就是保存到key中的数据的指针
  14. }
  15. void* thread_func(void* arg)
  16. {
  17. tsd* value = (tsd*)malloc(sizeof(tsd));
  18. value->tid = pthread_self();
  19. value->str = (char*)arg;
  20. //设置key的值
  21. printf("%s set the value[%p] of the key\n", (char*)arg, value);
  22. pthread_setspecific(key, value);
  23. //获取key中的值
  24. sleep(2);
  25. value = (tsd*)pthread_getspecific(key);
  26. printf("tid=0x%x, str=%s\n", value->tid, value->str);
  27. return NULL;
  28. }
  29. int main()
  30. {
  31. key = pthread_key_create(&key, destroy_func);
  32. pthread_t t1;
  33. pthread_t t2;
  34. pthread_create(&t1, NULL, thread_func, (void*)"thread1");
  35. pthread_create(&t2, NULL, thread_func, (void*)"thread2");
  36. pthread_join(t1, NULL);
  37. pthread_join(t2, NULL);
  38. return 0;
  39. }

4 线程池实现

线程池中有多个线程,用于以下用途:

imgclip13.png

线程池本质上也是生产者和消费者模型,生产者线程向任务队列添加任务,一旦有任务到了,如果有等待线程就唤醒执行任务;如果没有等待线程并且线程池未达到阈值,就创建新线程执行任务。