在POSIX线程(pthread)的情况下,程序开始运行时,它是以单进程的单个控制线程启动的。也就说说,一个进程至少包含一个线程。
多线程一般需要包含pthread.h头文件。
1、线程的创建
如果要创建多线程,则使用如下函数:
int pthread_create( pthread_t * thread,
const pthread_attr_t * attr,
void * (*start_rtn)(void*) ,
void * arg );
参数说明:成功时函数返回0,否则返回错误代码。
pthread_t * thread:thread指向的内存被设置为新线程id;
attr是新创建线程的属性,一般可以设置为默认,用NULL即可;
start_rtn为新线程开始执行的地址,其实就是一个函数指针,这个函数参数为void*,返回类型也是void*;
arg为新线程的参数,类型为void*。只能传递一个参数,如果有多个参数,则需要用一个结构体。
2、线程的终止
如果线程调用了exit、_Exit或者_exit,那么不仅这个线程会终止,整个进程也将终止!
单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止:
a. 线程只是从启动函数处返回,返回值是线程的退出码;
b.线程可以被同一进程的其他线程取消;
c.线程调用pthread_exit()函数。
void pthread_exit( void * retval);
返回后,retval保存着线程的返回值,这个值并不会随着线程的终止而自动释放(多个线程是共享数据段的),而应该使用pthread_join来清理现场。
int pthread_join( pthread_t thread, void ** retv_ptr );
调用pthread_join的函数将一直阻塞,直到指定的线程结束(上面三种情况之一)。
如果对线程返回值没有兴趣,则把retv_ptr设置为NULL即可。
如果线程只是从启动函数返回,retv_ptr将包含返回码;如果线程被取消,retv_ptr指定的内存就被设置为PTHREAD_CANCELED。
所要join的线程必须所joinable的(默认是);如果线程是游离(detach)状态,则pthread_join调用失败,返回EINVAL(游离态的线程结束时自动回收资源)。
3、线程的同步
线程的同步可以使用pthread提供的互斥接口来保护数据。互斥量(mutex)本质是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。
互斥变量用pthread_mutex_t数据类型来表示,是一个结构。创建时有两种方式:静态和动态。
静态方式:
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
PTHREAD_MUTEX_INITIALIZER是一个结构体常量。
动态方式:使用pthread_mutex_init和pthread_mutex_destory。
int pthread_mutex_init( pthread_mutex_t * lock, //需要初始化的互斥量
const pthread_mutexattr_t * attr);///互斥量的属性
int pthread_mutex_destroy( pthread_mutex_t * lock);//销毁互斥量
如果成功,则返回0;否则返回错误编号。
动态创建的互斥量不使用时必须销毁,否则会导致内存泄漏。
互斥量的加锁需要用到下面三个函数:
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,如果互斥量已经上锁,调用线程将会阻塞直到互斥量被解锁。对互斥量解锁使用pthread_mutex_unlock。
如果不希望线程阻塞,可以使用pthread_mutex_trylock尝试对互斥量加锁。如果此时互斥量未加锁,那么pthread_mutex_trylock将锁住互斥量,不阻塞并返回0;否则,pthread_mutex_trylock失败,不能锁住互斥量,函数返回EBUSY。
4、死锁
线程1和2都需要资源A和B,如果1首先获得A,然后2获得B。当1请求B时,将无法获得B,同时2也无法获得A。这时1、2两个线程将永远僵持下去,直到有外力改变这种状态为止。这时便发生了死锁。
有一种简单的发生死锁的情况:当同一个线程2次对同一个互斥变量加锁时将发生死锁。也就是不能递归加锁。为避免这种情况,可以将互斥量属性设置为recursive的。
//递归加锁将发生死锁
pthread_mutex_lock(mutex);
pthread_mutex_lock(mutex);
...
pthread_mutex_unlock(mutex);
pthread_mutex_unlock(mutex);
设置互斥量recursive属性以避免递归加锁时发生死锁:
///设置互斥锁属性为recursive
pthread_mutexattr_t attr;
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(mutex,&attr);