本文整理自(http://blog.csdn.net/jiajun2001/article/details/12624923)
多线程
1.头文件
#include<pthread.h> |
2.函数说明
2.1 pthread_create()
Int pthread_create(pthread_t *thread, pthread_attr_t *attr,void *(*start_routine)(void *), void *arg) | |||
Using | Parm | Ret | Others |
创建一个线程 | 1. thread 输出:创建线程句柄 2. attr 输入:设置线程属性,一般设置为NULL。见附录 3. start_routine 输入:线程处理函数 4. arg 输入:线程处理函数的参数 | 0:成功 |
|
2.2 pthread_join()
Int pthread_join(pthread_t th,void **thread_return) | |||
Using | Parm | Ret | Others |
线程同步:等待一个线程结束执行本线程。 | 1. th 输入:要等待结束的线程句柄,pthread_create() 的第一个参数 2. thread_return 输出:多线程函数返回值 | 0:成功 |
|
2.3 pthread_exit(void *retal)
void pthread_exit(void *retval); | |||
Using | Parm | Ret | Others |
终止一个线程,返回其他线程 | 1. retval 输出:线程通过调用: pthread_exit函数终止执行,并返回一个指向某对象的指针。注意:决不能用它返回一个指向局部变量的指针,因为线程调用该函数后,这个局部变量就不存在了,这将引起严重的程序漏洞 |
| 1.该函数在线程处理函数中被调用,用于终终止并收回该线程。 |
3.实例
4.附录
4.1 线程的合并与分离
背景:线程属于系统资源,占据一定的内存空间。在c或者c++编程中如果通过malloc()或者new分配了一块内存,就必须使用free()或者delete来回收这两块内存,否则会产生注明的内存泄露问题。线程也是如此,有创建就必须得有回收,线程的合并与分离由此而出。 合并: 合并是一种主动回收线程资源的方案。当一个进程或者线程调用了针对其他线程的pthread_join()接口,就是线程合并了。这个接口会阻塞调用进程或线程,知道被合并的线程结束为止。当被合并线程结束,pthread_join()接口就会回收这个线程的资源,并将这个线程的返回值返回给合并者。 分离:线程的分离是将线程资源的回收工作交由系统自动来完成,这也就是使得pthread_detach()接口只要拥有一个参数就行了,那就是被分离线句柄。 区别:在使用pthrea_join()后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,主线程并不希望因为调用pthread_join()而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码:pthread_detach(pthread_self())或者在父线程中调用Pthread_detach(thread_id)这将该子线程的状态设置为detached,则改线程运行结束后会自动释放所有的资源。 |
4.2 线程的属性
在pthread_create()函数中可以指定线程的属性,这个属性由一个线程属性对象(attr)来描述。线程属性对象由pthread_attr_init()接口初始化,并由pthrea_attr_destory()来销毁,完整定义如下: | |
Int pthread_attr_init(pthread_attr_t *attr); Int phtread_addr_destory(pthread_attr_t *attr); | |
1.绑定属性:将某个线程绑定在某个轻进程上。被绑定的线程据欧较高的相应速度,因为操作系统的调用主体是轻进程,绑定线程可以保证在需要的时候它总有一个轻进程可用。 (轻进程和linux系统的内核线程拥有相同的概念,术语内核的调度实体,一个轻进程可以控制一个或多个线程) | int pthread_attr_setscope(pthread_attr_t *att,int scope);
attr:线程属性对象。 Scope: PTHREAD_SCOPE_SYSTEM(绑定的) PTHREAD_SCOPE_PROCESS(非绑定) |
2.分离属性:前面说过的线程能够被合并和分离,分离属性就是让线程在创建之前就决定它应该是分离的。如果设置了这个属性,就没有必要调用pthread_join()或pthread_detach()来回收线程资源了。 | Int pthread_attr_setdetachstat(pthread_attr_t *att,int detachstate); attr:线程属性对象。 Detachstate: PTHREAD_CREATE_DETACHED(分离的) PTHREAD_CREATE_JOINABLE(可合并的,默认) |
3.调度属性:线程调度属性有三个:算法、优先级和继承权。 算法:轮询、先进先出、其他(默认) 优先级:linux优先级线程和进程不同。Linux线程优先级是从1-99的数值,数值越大优先级越高。而且只有在轮询或者先进先出调度算法时优先级才有效。其他算法优先级恒为0(此外需要root运行,并且放弃父线程的继承权,即父线程的调度属性) | pthread_attr_setschedpolicy(pthread_attr_t *attr,struct sched_policy) //设置算法 SCHED_RR(轮询)、SCHED_FIFO(先进先出)和SCHED_OTHER(其他) Pthread_attr_setscedparam(pthread_attr_t *attr,struct sched_param *parm) //设置优先级 Sched_param的sched_priority字段设置优先级。 int pthread_attr_Setinheritsched(pthread_attr*attr,int inheritsched) //设置继承权 PTHREAD_INHERIT_SCHED(拥有继承权) PTHREAD_EXPLICIT_SCHED(放弃继承权) |
4.堆栈大小:线程的主函数与程序的主函数main()有一个很相似的特性,那就是可以拥有局部变量。虽然同一个京城的线程之间是永祥内存空间的,但是他的局部变量却并不共享。原因就是局部变量存储在堆栈中,而不同的线程拥有不同的堆栈。Linux系统为每个线程默认分配了8MB的堆栈空间,如果觉得这个空间不够用,可以通过修改线程的堆栈大小属性进行扩容 | Int ptread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize) Stacksize:以字节为单位的堆栈大小,线程堆栈不鞥呢小于16KB而且尽量按4KB(32位系统)或2MB(64位系统)的整数倍分配,也就是内存页面大小的整数倍。此外,修改线程堆栈大小是有风险的。 |
5.满栈警戒区:linux为线程堆栈设置了一个满栈警戒区,这个区域一般就是一个页面,术语线程堆栈的一个扩展区域,一旦有代码访问了这个区域就会发出SIGSEGV信号进行通知。虽然满栈警戒区可以起到安全作用,但是也有利弊,就是会白白浪费掉内存空间,对于内存紧张的系统会使系统变得很慢。所以就有了关闭这个警戒区的需求。同时,如果我们修改了线程堆栈的大小,那么系统会认为我们会自己管理堆栈,也会将警戒区取消掉,如果有需要就开其他。 | Int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guadsize); |
4.3 线程同步
4.3.1互斥锁
互斥锁就是线程之间相互排斥,获得资格的线程排斥其他没有获得资源的线程。Linux使用互斥锁来实现这种机制。在加锁与解锁之间的代码区域叫做临界区,同步执行破坏了线程并行性的初衷,临界区越大破坏得越厉害。所以在实际应用中,应该避免有临界区的出现,实在不行,临界区也要尽量的小。如果缩小临界区都做不到,那还要多线程干嘛? | |
初始化和销毁互斥锁(主线程中调用) | Int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t * attr); Int pthread_mutex_destory(pthread_mutex_t *mutex); |
加锁与解锁(线程中调用) Trylock有点特殊,调用它试图加锁的线程永远不会被系统”拍晕”,只是通过EBUSY来告诉程序员这个锁已经有人用了 | Int pthread_mutex_lock(pthread_mutex_t *mutex) Int pthread_mutex_trylock(pthread_mutex_t *mutex) Int pthread_mutex_unlock(phtread_mutex_t *mutex); |
4.3.2实例