您的位置:首页 > 编程语言

Unix环境高级编程--第十一章--线程

2016-12-20 18:40 232 查看
1. 线程的作用

(1)  对于程序设计而言:当我们的一个进程在某一时刻,需要做的事件不止一件的时候,一般有两种方法。一种是采用异步编程的模式,一种是采用多线程同步模式。但是多线程同步模式远远比异步模式要方便的多。但是对于单核系统,往往异步编程模式效率更高。

(2)  对于交互程序,一般都是要多线程,因为在处理数据的时候,界面都是响应的。

每个线程都包含有表示执行环境所必须的信息,其中包括进程中标识线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、error变量以及线程私有数据。一个进程的所有信息都是对该进程的所有线程都是共享的。

2. 线程标识

#include <pthread.h>

//比较线程ID是否相等:0---不等;非0---相等
int pthread_equal(pthread_t t1, pthread_t t2);

//获得线程自身的ID
pthread_t pthread_self();


3. 线程创建

/*
创建线程:0---成功;否则,返回错误编号
成功时,新创建的线程ID会被设置成 th 指向的内存单元。
attr 参数用于定制各种不同的线程属性,NULL---默认属性。
新创建的线程从 func 函数的地址开始运行,
该函数只有一个无类型的指针参数 arg
*/
int pthread_create(pthread_t *th,
const pthread_attr_t *attr,
void *(*func)(void *),
void *arg);
//注:线程创建时并不能保证哪个参数会先运行:是创建线程还是调用线程。


4. 线程终止

线程的三种退出方式:

(1)可以简单地从启动例程中返回,返回值是线程的退出码

(2)线程可以被同一进程中的其他线程取消

(3)线程调用pthread_exit

//退出当前线程:res 跟 pthread_create() 函数中的 void* arg 类似。
//进程中的其他线程可以通过使用 pthread_join() 函数获得这个指针
void pthread_exit(void *res);

/*
阻塞等待线程结束函数:调用线程将一直阻塞,直到指定的线程调用
pthread_exit、从启动例程中返回或者被取消. 0---成功,否则错误编号。
如果线程简单的从它的启动例程中返回,res就包含返回码;
如果线程被取消,res指定的内存单元就被设置为 PTHREAD_CANCELED
*/
int pthread_join(pthread_t t, void **res);

//线程可以通过调用该函数来请求取消同一进程中的其他线程.
//注:pthread_cancel并不等待线程终止,它仅仅退出请求
int pthread_cancel(pthread_t t);

//线程可以安排它退出时需要调用的函数,一个线程可以安排多个这样的函数,
//这样的函数记录在栈中,执行时与注册顺序相反。这种函数叫清理函数
void pthread_cleanup_push(void (*rtm)(void *), void *arg);
void pthread_cleanup_pop(int execute);
/*
注:如果 execute 参数设置为0,清理函数将不被调用。
当调用 pthread_exit()时,响应取消请求时,用非零参数调用
pthread_cleanup_pop时,pthread_cleanup_pop都将删除“上次”
pthread_cleanup_push调用建立的清除处理函数,所以这两个函数必须
在与线程相同的作用域中以配对形式使用
而如果线程是简单的从启动例程中返回的而终止的话,它的清除处理函数
就不会被调用。而且由于平台不同,这种返回在FreeBSD和Mac OS X上回出现
段错误,解决方法使用pthread_exit().
*/

//下面这个进程函数,如果 condition==true 从中间返回,那么
//func_clean 就不会被执行
void *func(void *arg){
pthread_cleanup_push(func_clean, func_clean_arg);
if(condition)
{
return((void *)1);
}
pthread_cleanup_pop(0);
return((void *)1);
}

//如果线程已经分离,调用pthread_join会产生未定义的行为。
//我们调用pthread_detach来分离线程. 0---成功,否则返回错误编号
int pthread_detach(pthread_t t);
5. 线程同步

(1) 互斥量

/*
互斥变量是用 pthread_mutex_t 数据类型表示的,在使用互斥变量之前
必须首先对其进行初始化。对于静态分配的互斥量(静态或全局)可以设置为常量
PTHREAD_MUTEX_INITIALIZER(只适用于静态分配的互斥量),也可以
通过 pthread_mutex_init() 初始。以下:0---成功,否则返回错误编号
*/
int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a);
int pthread_mutex_destroy(pthread_mutex_t *m);

//加锁、解锁:0---成功,否则返回错误编号
int pthread_mutex_lock(pthread_mutex_t *m);
int pthread_mutex_trylock(pthread_mutex_t *m);
int pthread_mutex_unlock(pthread_mutex_t *m);
(2) 避免死锁

如果同一个线程试图对同一个互斥量加锁两次,就会出现死锁。另外,如果线程1获得了互斥量A,又要请求互斥量B,而线程2在获得了互斥量B又要请求A,那么就会出现死锁。第二种情况下,我们可以使用 pthread_mutex_trylock 接口避免死锁。如果已经占有某些锁而且pthread_mutex_trylock接口返回成功,那么就可以前进。但如果不能获取锁,可以先释放已经占有的锁,做好清理工作,然后过一段时间再重新试。或者,我们可以总是让A、B互斥量以相同的顺序加锁来避免死锁。

同时,注意锁的粒度也不要太粗或太细,而影响程序的并发性或增加系统的开销。

(3) 函数pthread_mutex_timedlock

/*
当线程试图获取一个已加锁的互斥量时,该函数允许绑定线程阻塞时间。
但是在达到超时时间时,pthread_mutex_timedlock不会对互斥量
进行加锁,而是返回错误码 ETIMEDOUT;但是如果在指定时间内互斥量
被解锁,那么该函数执行成功后会将该互斥量锁住
返回值:0---成功,否则返回错误编号
*/
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *m, const timespec *ts);
//注:这个时间指的是绝对时间,就是如果在这个截止时间之前 m 被解锁,
//那么这个函数立即返回,不会死等到指定的结束时间
(4) 读写锁

读写锁允许更高的并行性。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。当读写锁是写加锁的状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞。当读写锁在读加锁状态时,所有试图以读模式对他对它进行加锁的线程都可以得到访问,但是任何以写模式对此锁进行加锁的线程都会阻塞,直到所有的线程释放它们的读锁为止。但是当读写锁处于读模式锁住的状态,而这时有一个线程试图以写模式获取该锁时,读写锁通常会阻塞随后的读模式锁请求。

//读写锁:0---成功,否则返回错误编号
//对于静态分配的互斥量可以设置为常量
//PTHREAD_RWLOCK_INITIALIZER(只适用于静态分配的互斥量)
int pthread_rwlock_init(pthread_rwlock_t *rwlock_,
const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *l);

//加锁、解锁:0---成功,否则返回错误编号
int pthread_rwlock_rdlock(pthread_rwlock_t *l);
int pthread_rwlock_wrlock(pthread_rwlock_t *l);
int pthread_rwlock_unlock(pthread_rwlock_t *l);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *l);
int pthread_rwlock_trywrlock(pthread_rwlock_t *l);
(5) 带有超时的读写锁

4000

//带有超时的读写锁:0---成功,否则返回错误编号
int pthread_rwlock_timedrdlock(pthread_rwlock_t *l, const timespec *ts);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const timespec *ts);
(6) 条件变量


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: