中山有做网站的公司吗seo教程技术优化搜索引擎
多线程编程方法的形成
一方面,由于进程占有系统很多资源且独立运行,所以进程创建、撤销与切换的时空开销非常大。
另一方面,对称多处理机(SMP)允许多个进程并行,而进程并行开销很大。
举例来说,一个web浏览器在显示图像和文本的同时需要从网络接收数据,在单处理机上,按照基于进程来设计的话,那么就需要一个进程完成图像和文本的显示,另一个进程来从网络接收数据,从静态的角度思考,这两个进程的代码和数据有大部分是相同的,因为他们都属于web浏览器这个主体;从动态的角度考虑,当网络数据到来时,操作系统不得不控制执行网络数据的接收,图像和文本的显示会被暂停,进程切换带来时空浪费。
出于减小开销和提高效率的考虑,出现了新的可独立运行的基本单位-线程。线程属于进程,它就是一个进程内部的可独立运行的控制流,对于一个拥有多个线程的进程,这些线程共享大部分的进程资源,如代码、数据和文件等,当然,每个线程有自己的调用栈,线程与进程之间的关系表示如下图:
从空间的角度看,线程组内的各个线程共享所属进程的内存和资源,这种策略,一方面,更利于线程间的上下文切换和线程间的通信,另一方面,减少了内存和资源分配,创建和切换线程更加经济。
从时间的角度看,多个线程在同一个进程地址空间运行,增强了操作的并行性,能够更好更快的响应用户请求,如一个线程处理用户接口,另一个线程处理数据库访问,那么就可以在执行用户大量查询的同时仍然响应用户的输入。
线程库
POSIX线程标准定义了创建和操作线程的一套API,是线程行为的规范即Pthreads,Linux系统提供了库实现该规范,俗称为linux系统线程库。
早期版本的Linux(如linux2.4)内核没有提供线程库即内核空间没有提供多线程机制,使用的线程库是属于用户级的,库内的代码和数据结构在用户空间,其形式上是一个函数库,但因其模拟了“线程”概念和属性,能提供给用户实现多线程并发,但实质上,对于N个线程,在内核空间只有一个进程。
后来IBM和Redhat公司在内核上实现了多线程机制,使得遵从POSIX线程标准的库Pthread存在于内核空间了,用户使用该库内的函数就是系统调用,使用命令“getconf GNU_LIBPTHREAD_VERSION”可以查看其版本,如下
Note:NPTL(Native POSIX Thread Library),使用man -k pthread或apropos pthread可以查找到当前manpages中关于pthread的手册
多线程模型
多线程模型用来表示用户线程和内核线程关系,包括多对一模型、一对一模型和多对多模型等。
前面我们提到过早期的多线程只是在用户空间上实现,N个用户线程其实对应一个内核进程(轻量级线程),多对一模型就是类似于这种情形,由于它无法利用多个处理核,所以,几乎没有系统在用该模型了,模型如图所示
为每一个用户线程映射一个内核线程,就是一对一模型,这很好理解。
对于多对一模型,一个线程执行阻塞系统调用时,整个进程将会阻塞,内核只能一次调度一个线程,并未增加并发性。对于一对一模型,创建一个用户线程就要创建一个内核线程,由于过多的内核线程创建会降低系统性能,系统会限制创建的线程数量,那么考虑到上述两种模型的这些缺陷,提出来多对多模型,即多个用户级线程通过多路复用相同数量或更少数量的内核线程,示意图如下:
多线程问题及其编程实例
先看一个示例,该实例通过一个独立线程来计算非负整数的累加和。源码请参考我的Github项目TestUapi,路径为https://github.com/qinxiangke/UApiTest/tree/master/thd,文件名为thrd_ex1.c。
这个示例程序包含两个:初始(父)线程即main()和累加和线程即runner(),采用分叉-连接策略:初始线程使用pthread_join函数等待累加和线程的完成,累加和线程完成求和计算后调用pthread_exit退出,回到父线程输出累加和的值。核心的要点是创建线程函数pthread_create(),为了创建一个线程,我们需要
第一,定义新线程的功能或操作(如计算累加和),写一个函数来实现(如runner)。
第二,明确该功能或操作需要注入的输入,即函数的入口数据。
第三,线程属性初始化,如堆栈大小,调度信息等。
引入多线程后,直接影响到了fork()、exec()等系统调用的语义,如程序中的某个线程使用了fork()来创建进程,那么新建的进程是复制其所属线程还是会复制向上的所有线程,这就会因系统实现而已。
还有就是信号的传递及其对传递过来的信号的处理问题,对单线程的进程而言,必然就是传递给该进程,而对多线程进程而言,过来的信号是要传递到所有线程或自适应的一个线程或用户定义的某个线程还是某些线程,操作系统对此必须给出一个策略,首先,对于一个合法的信号,操作系统都有缺省的处理程序,当然也允许用户自定义作为替代。然后,依信号的类型而定,对于同步信号,原地处理即交给该信号的产生线程;对于异步信号(如),会交给所有线程。
此外还有线程撤销、线程本地存储和调度激活等问题,不再详述,有兴趣的可以去了解。
下面,对于小节开头给出的实例,进行功能扩充,需求变更为:
- 计算一组数字的多种统计值,这组数字通过命令行传递
- 创建三个工作线程,分别求数字的平均值,最大值和最小值
- 各种统计值作为全局变量由各个工作线程来设置,当工作线程退出时,父线程来输出这些值。
请您试着写一个实现以上需求的多线程程序。
这是我写的一种实现,代码在我的GitHub项目TestUapi,路径为https://github.com/qinxiangke/UApiTest/tree/master/thd,文件名为thrd_ex2.c,写得不好,请多多指正