1. 线程的概念
-
文件描述符表
-
每种信号的处理方式(
SIG_IGN
、SIG_DFL
或者自定义的信号处理函数) -
当前工作目录
-
用户id和组id
但有些资源是每个线程各有一份的:
-
线程id
-
上下文,包括各种寄存器的值、程序计数器和栈指针
-
栈空间
-
errno
变量 -
信号屏蔽字
-
调度优先级
2. 线程控制
#include <pthread.h> int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);
返回值:成功返回0,失败返回错误号。以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno
中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno
,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。
pthread_create
的函数指针
start_routine
决定。
start_routine
函数接收一个参数,是通过
pthread_create
的
arg
参数传递给它的,该参数的类型为
void *
,这个指针按什么类型解释由调用者自己定义。
start_routine
的返回值类型也是
void *
,这个指针的含义同样由调用者自己定义。
start_routine
返回时,这个线程就退出了,其它线程可以调用
pthread_join
得到
start_routine
的返回值,类似于父进程调用
wait(2)
得到子进程的退出状态
。
2.2. 终止线程
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
-
从线程函数
return
。这种方法对主线程不适用,从main
函数return
相当于调用exit
。 -
一个线程可以调用
pthread_cancel
终止同一进程中的另一个线程。 -
线程可以调用
pthread_exit
终止自己。
用pthread_cancel
终止一个线程分同步和异步两种情况,比较复杂。下面介绍pthread_exit
的和pthread_join
的用法。
#include <pthread.h> void pthread_exit(void *value_ptr);
value_ptr
是void *
类型,和线程函数返回值的用法一样,其它线程可以调用pthread_join
获得这个指针。
需要注意,pthread_exit
或者return
返回的指针所指向的内存单元必须是全局的或者是用malloc
分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
#include <pthread.h> int pthread_join(pthread_t thread, void **value_ptr);
返回值:成功返回0,失败返回错误号
调用该函数的线程将挂起等待,直到id为thread
的线程终止。thread
线程以不同的方法终止,通过pthread_join
得到的终止状态是不同的,总结如下:
-
如果
thread
线程通过return
返回,value_ptr
所指向的单元里存放的是thread
线程函数的返回值。 -
如果
thread
线程被别的线程调用pthread_cancel
异常终止掉,value_ptr
所指向的单元里存放的是常数PTHREAD_CANCELED
。 -
如果
thread
线程是自己调用pthread_exit
终止的,value_ptr
所指向的单元存放的是传给pthread_exit
的参数
pthread_join
获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用
pthread_join
,这样的调用将返回
EINVAL
。对一个尚未detach的线程调用
pthread_join
或
pthread_detach
都可以把该线程置为detach状态,也就是说,不能对同一线程调用两次
pthread_join
,或者如果已经对一个线程调用了
pthread_detach
就不能再调用
pthread_join
了。
3. 线程间同步
Mutex用pthread_mutex_t
类型的变量表示,可以这样初始化和销毁:
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex); int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
返回值:成功返回0,失败返回错误号。
pthread_mutex_init
函数对Mutex做初始化,参数attr
设定Mutex的属性,如果attr
为NULL
则表示缺省属性。用pthread_mutex_init
函数初始化的Mutex可以用pthread_mutex_destroy
销毁。如果Mutex变量是静态分配的(全局变量或static
变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER
来初始化,相当于用pthread_mutex_init
初始化并且attr
参数为NULL
。Mutex的加锁和解锁操作可以用下列函数:
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号。
一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。
如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。