线程
一、概念:
线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。
线程,轻量级,不拥有自己独立的内存资源,共享进程的代码区、数据区、堆区(注意没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、当前目录、用户ID和组ID等资源。
线程拥有自己独立的栈,因此也有自己独立的局部变量。
一个进程可以同时拥有多个线程,即同时被系统调度的多条执行路线,但至少要有一个主线程。
二、线程函数
1.创建线程
int pthread_create (pthread_t* restrict thread,
const pthread_attr_t* restrict attr,
void* (*start_routine) (void*),
void* restrict arg);
thread- 线程ID,输出参数。
pthread_t即unsigned long int。
attr - 线程属性,NULL表示缺省属性。
pthread_attr_t可能是整型也可能是结构,
因实现而异。
start_routine- 线程过程函数指针,
参数和返回值的类型都是void*。
启动线程本质上就是调用一个函数,
只不过是在一个独立的线程中调用的,
函数返回即线程结束。
arg - 传递给线程过程函数的参数。
线程过程函数的调用者是系统内核,
而非用户代码,
因此需要在创建线程时指定参数。
2.等待线程
int pthread_join (pthread_t thread, void** retval);
等待thread参数所标识的线程结束,成功返回0,失败返回错误码。
3.获取线程自身的ID
pthread_t pthread_self (void);
成功返回调用线程的ID,不会失败。
4.比较两个线程的ID
int pthread_equal (pthread_t t1, pthread_t t2);
若参数t1和t2所标识的线程ID相等,则返回非零,否则返回0。
5.终止线程
① 从线程过程函数中return。
② 调用pthread_exit函数。
void pthread_exit (void* retval);
retval - 和线程过程函数的返回值语义相同。
注意:在任何线程中调用exit函数都将终止整个进程。
6.线程执行轨迹
① 同步方式(非分离状态):
创建线程之后调用pthread_join函数等待其终止,并释放线程资源。
② 异步方式(分离状态):
无需创建者等待,线程终止后自行释放资源。
int pthread_detach (pthread_t thread);
使thread参数所标识的线程进入分离(DETACHED)状态。
处于分离状态的线程终止后自动释放线程资源,且不能被pthread_join函数等待。
成功返回0,失败返回错误码。
7.取消线程
① 向指定线程发送取消请求
int pthread_cancel (pthread_t thread);
成功返回0,失败返回错误码。
注意:该函数只是向线程发出取消请求,并不等待线程终止。
缺省情况下,线程在收到取消请求以后,并不会立即终止,而是仍继续运行,直到其达到某个取消点。
在取消点处,线程检查其自身是否已被取消了,并做出相应动作。
当线程调用一些特定函数时,取消点会出现。
② 设置调用线程的可取消状态
int pthread_setcancelstate (int state,int* oldstate);
成功返回0,并通过oldstate参数输出原可取消状态(若非NULL),失败返回错误码。
state取值:
PTHREAD_CANCEL_ENABLE - 接受取消请求(缺省)。
PTHREAD_CANCEL_DISABLE - 忽略取消请求。
③ 设置调用线程的可取消类型
int pthread_setcanceltype (int type, int* oldtype);
成功返回0,并通过oldtype参数输出原可取消类型,(若非NULL),失败返回错误码。
type取值:
PTHREAD_CANCEL_DEFERRED - 延迟取消(缺省)。
被取消线程在接收到取消请求之后并不立即响应,
而是一直等到执行了特定的函数(取消点)之后再响应该请求。
PTHREAD_CANCEL_ASYNCHRONOUS - 异步取消。
被取消线程可以在任意时间取消,不是非得遇到取消点才能被取消。
但是操作系统并不能保证这一点。
线程同步
一、互斥锁(mutex)
通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。
1 int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr); 2 int pthread_mutex_lock(pthread_mutex_t* mutex); 3 int pthread_mutex_destroy(pthread_mutex_t * mutex); 4 int pthread_mutex_unlock(pthread_mutex_t * mutex);
① 互斥量被初始化为非锁定状态
② 线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
③ 线程2调用pthread_mutex_lock函数,阻塞等待;
④ 线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
⑤ 线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态
特点:
互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,
直到这个线程unlock,其他的线程才开始可以利用这 个资源。
二、信号量(sem)
如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。
信号量是一个计数器,用于控制访问有限共享资源的线程数。
1 // 创建信号量 2 int sem_init (sem_t* sem, int pshared,unsigned int value); 3 sem - 信号量ID,输出。 4 pshared - 一般取0,表示调用进程的信号量。 5 非0表示该信号量可以共享内存的方式, 6 为多个进程所共享(Linux暂不支持)。 7 value - 信号量初值。 8 9 // 信号量减1,不够减即阻塞 10 int sem_wait (sem_t* sem); 11 12 // 信号量减1,不够减即返回-1,errno为EAGAIN 13 int sem_trywait (sem_t* sem); 14 15 // 信号量减1,不够减即阻塞, 16 // 直到abs_timeout超时返回-1,errno为ETIMEDOUT 17 int sem_timedwait (sem_t* sem, 18 const struct timespec* abs_timeout); 19 20 struct timespec { 21 time_t tv_sec; // Seconds 22 long tv_nsec; // Nanoseconds [0 - 999999999] 23 }; 24 25 // 信号量加1 26 int sem_post (sem_t* sem); 27 28 // 销毁信号量 29 int sem_destroy (sem_t* sem);
特点:
信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作
(大家都在semtake的时候,就阻塞在哪里)
三、条件变量(cond)
条件变量可以让调用线程在满足特定条件的情况下暂停。
int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr); //亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 使调用线程睡入条件变量cond,同时释放互斥锁mutex int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex); int pthread_cond_timedwait (pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime); struct timespec { time_t tv_sec; // Seconds long tv_nsec; // Nanoseconds [0 - 999999999] }; // 从条件变量cond中唤出一个线程, // 令其重新获得原先的互斥锁 int pthread_cond_signal (pthread_cond_t* cond); 注意:被唤出的线程此刻将从pthread_cond_wait函数中返回, 但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。 // 从条件变量cond中唤出所有线程 int pthread_cond_broadcast (pthread_cond_t* cond); int pthread_cond_destroy (pthread_cond_t* cond);
四、