线程是程序中完成一个独立任务的完整执行序列,根据运行环境以及调度者的身份,可以分为内核线程以及用户线程。
- 内核线程,运行在内核空间,由内核来调度,有的系统上也称为LWP(轻量级进程)
- 用户线程,运行在用户空间,由线程库来调度
内核线程相当于用户线程运行的容器,当进程的一个内核线程获得CPU的使用权时,它就加载并运行一个用户线程。一个进程可以拥有M个内核线程和N个用户线程,根据内核线程和用户线程数量的不同,线程的实现方式可以分为以下三种:
- 完全在用户空间实现,对这种实现方式而言,M=1,即N个用户线程对应1个内核线程,该内核线程相当于进程本身。
- 优点:创建和调度线程都无须内核干预,因此速度相当快,并且它不占用额外的内核资源,即使一个进程创建了很多线程,也不会对系统性能造成明显的影响。
- 缺点:对于多处理器系统,一个进程的多个线程无法运行在不同的CPU上,因为内核是根据最小调度单位来分配CPU的,并且线程的优先级只对同一个进程中的线程有效,比较不同进程中的线程优先级没有意义。
- 完全由内核调度,该模式下将创建、调度线程的任务都交给了内核,这种方式下,M:N=1:1,即1个用户空间线程被映射为1个内核线程。并且其优缺点与上面完全在用户空间实现相反。
- 双层调度,是前两种实现模式的混合体,内核调度M个内核线程,线程库调度N个用户线程。其优点在于不但不会消耗过多的内核资源,而且线程切换的速度也比较快,同时它可以充分利用多处理器的优势。
一、创建线程与结束线程
1.1、pthread_create
创建一个线程的函数为:
#include <pthread.h>
/*pthread_t的类型描述,可见pthread_t为一个整数类型*/
typedef unsigned long int pthread_t;
int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
void* (*start_routine)(void*), void* arg);
- thread是新线程的标识符
- attr用于设置新线程的属性,传递NULL表示使用默认线程属性
- start_routine指定新线程将运行的函数
- arg则是运行函数的参数
pthread_create成功时返回0,失败返回错误码
1.2、pthread_exit
退出线程的函数,具体为:
#include <pthread.h>
void pthread_exit(void* retval);
pthread_exit函数通过retval参数向线程的回收这传递其退出信号,他执行完后不会返回到调用者,而且永远不会失败。
1.3、pthread_join
一个进程中的所有线程都可以调用pthread_join函数来回收其他线程(在目标线程可回收的前提下),这类似于回收进程的wait和waitpid。其定义如下:
#include <pthread.h>
int pthread_join(pthread_t thread, void** retval);
- thread是目标线程的标识符
- retval是目标线程返回的退出信息
该函数会一直阻塞,直到回收的线程结束为止。成功时返回0,失败时返回错误码。
1.4、pthread_cancel
取消线程(异常终止一个线程),函数如下:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
thread属性是目标线程的标识符。成功时返回0,失败返回错误码
二、线程属性
pthread_attr_t结构体定义了一套完整的线程属性。
#include <bits/pthreadtypes.h>
#define __SIZEOF_PTHREAD_ATTR_T 36
typedef union{
char __size[__SIZEOF_PTHREAD_ATTR_T];
long int __align;
}pthread_attr_t;
各种线程属性全部包含在了一个字符数组中,线程库定义了一系列函数来操作pthread_attr_t类型的变量,如下所示:
#include <pthread.h>
/*初始化线程属性对象*/
int pthread_attr_init(pthread_attr_t* attr);
/*销毁线程属性对象,被销毁的线程属性对象只有再次初始化之后才能继续使用*/
int pthread_attr_destory(pthread_attr_t* attr);
/*还有一些函数用以获取或者设置线程属性对象的某个属性
具体在P273,
P274还有各个属性的含义,此处不再赘述。
*/
《Linux高性能服务器编程》学习笔记