线程池

1.1, 第一版

在前面我们谈到过, 进程池设计和线程池设计的一些适用场景。

进程池设计:

  • 每个进程有独立的内存空间, 增加进程间的隔离性。一个进程崩溃不会影响到其它进程。

  • 进程间存在隔离性, 这种解耦促使业务逻辑方便书写。

  • 进程的创建和销毁比线程开销大, 占用的内存空间也比线程大。

  • 上下文的调度切换时间长。

  • 适合并发量低(IO请求数少), 业务复杂(CPU密集), 任务执行事件长的系统设计。

线程池设计:

  • 线程之间共享资源, 隔离新差, 一个线程极容易影响到另一个线程(数据同步和一致性)。

  • 但是隔离性差, 使得线程间通信比进程间通信要更方便。

  • 线程较轻量,创建和销毁的开销较小。

  • 适合并发量高(I/O密集型),内存使用要求高, 业务简单, 可以大量快速、轻量级任务处理的场景。

那么当我们真正在设计线程池的时候又该怎么实现那? 其实线程池的设计本质逻辑上和进程池并无太多异常, 只不过我们在线程间通信的是时候, 要远比进程池要简单的多.

1.1.1 设计逻辑

1.1.2 CODE

head.h

mian

mian.c

pool

pool.c

tcpInit

tcpInit.c

epoll

epoll.c

worker

worker.c

transFile

transFile.c

queue

queue.h

queue.c

1.2 第二版

1.2.1 线程池的退出: 异步拉起同步

我们不适合在多线程中使用信号(不适合), 因为信号是基于进程设计的, 我们无法确定代码在实际运行过程中, 到底进程中的那个线程收到并且执行了这个信号的处理逻辑, 这可能会导致一些异常问题. 总的来说, 信号的设计和线程的设计从某种程度上是不具有良好的相互适配性.

为了解决上面的问题, 我们可以通过异步拉起同步的方式:

1.2.2.1 CODE

main

main.c

1.2.2 线程退出: pthread_cancel

当主线程监听到父进程发过来的信号触发信息, 之后, 我们可以让主线从, 通过pthread_cancel函数,让子线程被动退出.

1.2.2.1 CODE

main

mian.c

但是当我们真正在代码执行的时候, (ps -elLf)我们发现好像子线程并没有完全退出

1.2.2.2 CODE: 改进方案

在上面的问题的基础上, 我们可以优化资源清理行为. 我们知道pthread_cancel函数导致线程退出, 会自动执行被退出线程的清理函数, 所以我们可以在加锁的时候, 增加清理函数, 防止死锁

worker

worker.c

1.2.3 更好的退出方式: 标记位退出

虽然我们上面想了一些办法, 可以使线程退出, 但是上面的退出方式本质上是一种被动退出的方式, 某种程度上这不是一个良好的退出方式, 因为子线程没有办法到底在那个代码运行节点退出, 这可能导致有些资源没有正常释放或者某些必要的逻辑进行了一半, 就使得线程退出.

所以在上述逻辑的基础上, 我们可以取消被动退出的方式, 转而使用一种, 以标志位标记的主动退出模式.

1.2.3.1 CODE

head

head.h: 增加退出标记位

main

main.c

worker

worker.c