POSIX线程基础


在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);
相关文章
相关标签/搜索