1.进程和线程之间的关系
线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。可以把线程看成是操作系统分配CPU时间的基本单元。一个进程可以拥有一个至多个线程。它包含有如下优点:
1)多进程情况下,每个进程有自己独立的地址空间,而在多线程的情况下,同一个进程内的线程共享进程的地址空间。创建一个新的线程耗费的资源要少很多。
2)系统调度方面,线程共享地址空间,因此线程间的切换速度要远远快于进程间的切换速度。
3)同一进程内的线程共享数据时,可以直接提供给其他线程使用,而不必经过操作系统,因此线程间通信更加方便和省时。
线程在进程内部共享地址空间、打开的文件描述符等资源。同时线程也有其私有的数据信息,包括:线程号、寄存器(程序计数器和堆栈指针)、堆栈、信号掩码、优先级、线程私有存储空间。
2.创建线程
linux系统支持POSIX多线程接口,称为pthread。编写linux下的多线程程序,需要包含头文件pthread.h,链接时需要使用库libpthread.a
如果在主线程里面创建线程,程序就会在创建线程的地方产生分支,变成两个部分执行。线程的创建通过函数pthread_create来完成。成功返回0
#include<pthread.h>
int pthread_create(pthread * thread, pthread_attr * attr , void * (*start_routine)(void *), void * arg);
thread: 参数是一个指针,当线程成功创建时,返回创建线程ID。
attr: 用于指定线程的属性
start_routine: 该参数是一个函数指针,指向线程创建后要调用的函数。
arg: 传递给线程函数的参数。
3.线程终止
两种方式终止线程。第一通过return从线程函数返回,第二种通过调用pthread_exit()函数使线程退出。需要注意的地方:一是,主线程中如果从main函数返回或是调用了exit函数退出主线程,则整个进程终止,此时所有的其他线程也将终止。另一种是,如果主线程调用pthread_exit函数,则仅仅是主线程消亡,进程不会结束,其他线程也不会结束,知道所有的线程都结束时,进程才结束。
4.私有数据
进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有。但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-specific Date)TSD来解决。在线程内部,私有数据可以被各个函数访问,但对其他线程是屏蔽的。
线程私有数据采用了一键多值的技术,即一个键对应多个数值,访问数据时好像是对同一个变量进行访问,但其实是在访问不同的数据。
创建私有数据的函数有4个:pthread_key_create(创建), pthread_setspecific(设置), pthread_getspecific(获取), pthread_key_delete(删除)。
5.线程同步
线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和异步信号。
1)互斥锁(mutex)通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。
2)条件变量(cond)是利用线程间共享的全局变量进行同步的一种机制。
3)异步信号,如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。
2.条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制。
条件变量宏观上类似if语句,符合条件就能执行某段程序,否则只能等待条件成立。使用条件变量主要包括两个动作;一个等待使用资源的线程在等待着“条件变量被设置为真”;另一个线程在使用完资源后“设置条件为真”,这样就可以保证线程之间的同步了。这样就存在一个关键的问题,就是要保证条件变量能被真正的修改,条件变量要受到特殊的保护,实际使用中互斥锁就扮演着这样一个保护的角色。linux也提供了一系列对条件变量操作的函数:
函数 | 功能 |
---|---|
pthread_cond_init | 初始化条件变量 |
pthread_cond_wait | 基于条件变量阻塞,无条件等待 |
pthread_cond_timedwait | 阻塞直到指定事件发生,计时等待 |
pthread_cond_signal | 接触特定线程阻塞,存在多个等待线程按入队顺序激活其中一个 |
pthread_cond_broadcast | 解除所有线程的阻塞 |
pthread_cond_destroy | 清除条件变量 |
下面这段话是在网上看到的
其实在Linux 中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone() 。该系统copy 了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy 过程和fork 不一样。copy 后的进程和原先的进程共享了所有的变量,运行环境(clone的实现是可以指定新进程与老进程之间的共享关系,100%共享就表示创建了一个线程)。这样,原先进程中的变量变动在copy后的进程中便能体现出来。