1. Linux多线程概述
1.1. 概述
进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理器的空转时间支持多处理器和减少上下文切换开销,也就出现了线程。
线程通常叫做轻量级进程。线程是在共享内存空间中并发执行的多道执行路径,是一个更加接近于执行体的概念,拥有独立的执行序列,是进程的基本调度单元,每个进程至少都有一个main线程。它与同进程中的其他线程共享进程空间{堆 代码 数据 文件描述符 信号等},只拥有自己的栈空间,大大减少了上下文切换的开销。
线程和进程在使用上各有优缺点:线程执行开销小,占用的CPU少,线程之间的切换快,但不利于资源的管理和保护;而进程正相反。从可移植性来讲,多进程的可移植性要好些。
同进程一样,线程也将相关的变量值放在线程控制表内。一个进程可以有多个线程,也就是有多个线程控制表及堆栈寄存器,但却共享一个用户地址空间。要注意的是,由于线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。
1.2. 线程分类
按调度者分为用户级线程和核心级线程
·用户级线程:主要解决上下文切换问题,调度算法和调度过程全部由用户决定,在运行时不需要特定的内核支持。缺点是无法发挥多处理器的优势
·核心级线程:允许不同进程中的线程按照同一相对优先调度方法调度,发挥多处理器的并发优势
现在大多数系统都采用用户级线程和核心级线程并存的方法。一个用户级线程可以对应一个或多个核心级线程,也就是“一对一”或“一对多”模型。
1.3. 线程创建的Linux实现
Linux的线程是通过用户级的函数库实现的,一般采用pthread线程库实现线程的访问和控制。它用第3方posix标准的pthread,具有良好的可移植性。 编译的时候要在后面加上 –lpthread
创建 退出 等待
多进程 fork() exit() wait()
多线程 pthread_create pthread_exit() pthread_join()
2. 线程的创建和退出
创建线程实际上就是确定调用该线程函数的入口点,线程的创建采用函数pthread_create。在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,线程就退出,这也是线程退出的一种方式。另一种线程退出的方式是使用函数pthread_exit()函数,这是线程主动退出行为。这里要注意的是,在使用线程函数时,不能随意使用exit退出函数进行出错处理,由于exit的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用pthread_exit函数来代替进程中的退出函数exit。
由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以通过wait()函数系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是pthread_join函数。pthread_join函数可以用于将当前线程挂起,等待线程的结束。这个函数是一个线程阻塞函数,调用它的函数将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。
函数原型:
#include <pthread.h>
int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);
void pthread_exit(void *retval);
通常的形式为:
pthread_t pthid;
pthread_create(&pthid,NULL,pthfunc,NULL);或pthread_create(&pthid,NULL,pthfunc,(void*)3);
pthread_exit(NULL);或pthread_exit((void*)3);//3作为返回值被pthread_join函数捕获。
函数pthread_create用来创建线程。返回值:成功,则返回0;失败,则返回-1。各参数描述如下:
·参数thread是传出参数,保存新线程的标识;
·参数attr是一个结构体指针,结构中的元素分别指定新线程的运行属性,attr可以用pthread_attr_init等函数设置各成员的值,但通常传入为NULL 即可;
·参数start_routine是一个函数指针,指向新线程的入口点函数,线程入口点函数带有一个void *的参数由pthread_create的第4个参数传入;
·参数arg用于传递给第3个参数指向的入口点函数的参数,可以为NULL,表示不传递。
函数pthread_exit表示线程的退出。其参数可以被其它线程用pthread_join函数捕获。
示例:
#include <stdio.h>
#include <pthread.h>
void *ThreadFunc(void *pArg) //参数的值为123
{
int i = 0;
for(; i<10; i++)
{
printf("Hi,I‘m child thread,arg is:%d\n", (int)pArg);
sleep(1);
}
pthread_exit(NULL);
}
int main()
{
pthread_t thdId;
pthread_create(&thdId, NULL, ThreadFunc, (void *)123 );
int i = 0;
for(; i<10; i++)
{
printf("Hi,I‘m main thread,child thread id is:%x\n", thdId);
sleep(1);
}
return 0;
}
编译时需要带上线程库选项:
gcc -o a a.c -lpthread