UNIX多线程学习笔记Word文件下载.docx
- 文档编号:18333133
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:35
- 大小:37.59KB
UNIX多线程学习笔记Word文件下载.docx
《UNIX多线程学习笔记Word文件下载.docx》由会员分享,可在线阅读,更多相关《UNIX多线程学习笔记Word文件下载.docx(35页珍藏版)》请在冰豆网上搜索。
条件操作有5种操作,创建,销毁,触发,广播和等待.
其它的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本操作封装处理.
线程基础函数列表
线程,互斥锁,条件在Linux平台上对应的API可以用表1归纳。
为了方便熟悉Windows线程编程的读者熟悉Linux多线程开发的API,我们在表中同时也列出WindowsSDK库中所对应的API名称。
表1线程基础函数列表
对象
操作
LinuxPthreadAP
WindowsSDK库对应API
线程
创建
pthread_create
CreateThread
退出
pthread_exit
ThreadExit
等待
pthread_join
WaitForSingleObject
互斥锁
pthread_mutex_init
CreateMutex
销毁
pthread_mutex_destroy
CloseHandle
加锁
pthread_mutex_lock
解锁
pthread_mutex_unlock
ReleaseMutex
条件
pthread_cond_init
CreateEvent
pthread_cond_destroy
触发
pthread_cond_signal
SetEvent
广播
pthread_cond_broadcast
SetEvent/ResetEvent
pthread_cond_wait/pthread_cond_timedwait
SingleObjectAndWait
第二节线程
2.1线程的创建与结束
2.1.1)pthread_t
线程的标示符类型,pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义
typedefunsignedlongintpthread_t;
2.1.2)pthread_create
intpthread_create(pthread_t*,
constpthread_attr_t*,
void*(*)(void*),
void*);
第一个参数为指向线程标识符的指针.
第二个参数用了设置线程属性,设置为空指针,这样生成默认属性的线程.
第三个参数是线程运行函数的起始地址
第四个参数是运行函数的参数.
当创建线程成功时,函数返回0,若不为0说明线程创建失败,创建错误返回代码为EAGAIN和EINVAL.前者表示系统限制创建的线程,例如线程数目过多;
后者表示第二个参数代表的线程属性值非法,线程创建成功后,新创建的线程运行参数三参数四确定的函数,原来得线程继续运行下一行代码.
2.1.3)pthread_join
函数pthread_join用来等待一个线程结束.
intpthread_join(pthread_t,void**);
第一个参数为被等待的线程标识符
第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值.
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回.
2.1.4)pthread_exit
线程除了正常执行结束外,还可通过函数pthread_exit来结束它
voidpthread_exit(void*);
唯一的参数是函数的返回码,只要pthread_join中得第二个参数不是NULL,这个值将会传递给pthread_join的第二个参数.
要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程返回成功,其余调用pthread_join的线程则返回错误ESRCH.
2.1.5)pthread_self
该函数获取自身线程的ID.
pthread_tpthread_self()
2.2修改线程的属性
线程属性结构为pthread_attr_t,在头文件/usr/include/pthread.h中定义.
线程属性初始化函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用.
线程属性对象包括是否绑定,是否分离,堆栈大小,优先级.默认属性为非绑定,非分离,缺省1M的堆栈,与父进程同样级别的优先级.
线程属性函数有:
对线程属性初始化/去除初始化
intpthread_attr_init(pthread_attr_t*attr);
intpthread_attr_destroy(pthread_attr_t*attr);
获取/修改线程的分离状态属性
intpthread_attr_getdetachstate(constpthread_attr_t*attr,int*detachstate);
intpthread_attr_setdetachstate(pthread_attr_t*attr,intdetachstate);
2.2.1)关于线程的绑定
线程的绑定,牵扯到另外一个概念,轻进程(LWP:
LightWeightProcess).轻进程可以理解为内核进程,它位于用户层和系统层之间,系统对线程资源的分配,对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程,启动多少个轻进程,哪些轻进程来控制哪些线程是由系统来控制的,这种状态即称为非绑定的。
绑定状况下,则顾名思义,即某个线程固定的绑定到一个轻进程上,被绑定的线程具有较高的响应速度,这是因为cpu时间片的调度是面向轻进程的。
绑定的线程可以保证在需要的时候它总有一个轻进程可用。
通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足实时反应之类的要求。
设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个时指向属性结构的指针,第二个参数是绑定类型PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)
下面的代码即创建一个绑定线程
pthread_attr_tattr;
pthread_ttid;
pthread_attr_init(&
attr);
/*初始化属性值,均设为默认值*/
pthread_attr_setscope(&
attr,PTHREAD_SCOPE_SYSTEM);
pthread_create(&
tid,&
attr,(void*)my_function,NULL);
2.2.2)关于线程的状态:
分离态/非分离态
线程的分离状态决定一个线程以什么样的方式来终止自己,在上面的例子中,我们采用了线程的默认属性,即非分离状态,在这种情况下,原有的线程等待创建的线程结束。
只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的资源。
而分离线程不是这样的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放资源。
应该根据自己的需要,选择合适的分离状态。
设置线程分离状态的函数:
pthread_attr_setdetachstate(pthread_attr_t*attr,intdetachstate)
第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD_CREAE_JOINABLE(非分离线程)
这里要主要的一点,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。
要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会,留出足够的时间让函数pthread_create返回。
设置一段等待时间,是再多线程编程里常用的方法,都是注意不要使用诸如wait()之类的函数,他们是使整个进程睡眠,并不能解决线程同步的问题.
使线程进入分离状态也可用如下函数
intpthread_detach(pthread_ttid);
2.2.3)关于线程的优先级
线程的优先级,它存放在结构sched_param中,用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行关联,一般来说,我们总是先取出优先级,对取得的值修改后再放回去。
线程的优先级取值范围为-20—20,值越大,其优先级越低,缺省值为0,这个参数仅当调度策略为实时(SCHED_RR或SCHED_FIFO)时才有效。
通过pthread_setschedparam()函数来改变
sched_paramparam;
intnewprio=20;
pthtead_attr_init(attr);
pthread_attr_getschedparam(&
attr,param);
param.sched_priority=newprio;
pthread_attr_setschedparam(&
pthrad_create(&
attr,(void*)myfunction,myarg);
第三节互斥锁
互斥锁是用来保证一段时间内只有一个线程在执行一段代码
pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,即同一时间只能被一个线程调用执行。
当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。
A线程先锁定互斥锁1,B线程先锁定互斥锁2,这是就出现了死锁。
此时我们可以使用函数pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。
另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计时注意这一点。
pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待
互斥锁函数有:
初始化一个互斥量
intpthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t*attr);
给一个互斥量加锁
intpthread_mutex_lock(pthread_mutex_t*mutex)
加锁,如果失败不阻塞
intpthread_mutex_trylock(pthread_mutex_t*mutex)
intpthread_mutex_unlock(pthread_mutex_t*mutex)
互斥量用pthread_mutex_t数据类型表示,在使用互斥量之前必须先多它初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),方法如下
pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER
也可以调用pthread_mutex_init函数进行初始化,如果动态的分配互斥量,那么释放内存前需要调用pthread_mutex_destory.
销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。
由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
互斥锁的属性
线程和线程的同步对象(互斥量,读写锁,条件变量)都具有属性。
在修改属性前都需要对结构进行初始化,使用完后再把该结构回收。
用pthread_mutexattr_init对互斥锁属性初始化,用pthread_mutexattr_destory对互斥锁属性结构回收。
intpthread_mutexattr_init(pthread_mutexattr_t*attr);
intpthread_mutexattr_destroy(pthread_mutexattr_t*attr);
互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。
当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:
*PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。
当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。
这种锁策略保证了资源分配的公平性。
*PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。
如果是不同线程请求,则在加锁线程解锁时重新竞争。
*PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。
这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
*PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争
pthread_mutexattr_init将属性对象的值初始化为缺省值,并分配属性对象占用的空间。
设置互斥锁属性函数:
1)intpthread_mutexattr_setpshared(pthread_mutexattr_t*mattr,intpshared)
用来设置互斥锁变量的作用域,
PTHREAD_PROCESS_SHARED:
在多个进程中的线程之间共享互斥锁,可以在共享内存中创建互斥锁.
PTHREAD_PROCESS_PRIVATE:
仅有那些由同一个进程创建的线程才能够处理该互斥锁
2)intpthread_mutexattr_settype(pthread_mutexattr_t*attr,inttype);
可用来设置互斥锁的type属性,类型属性的缺省值为PTHREAD_MUTEX_DEFAULT。
PTHREAD_MUTEX_NORMAL
此类型的互斥锁不会检测死锁。
如果线程在不首先解除互斥锁的情况下尝试重新锁定该互斥锁,则会产生死锁。
尝试解除由其他线程锁定的互斥锁会产生不确定的行为。
如果尝试解除锁定的互斥锁未锁定,则会产生不确定的行为。
PTHREAD_MUTEX_ERRORCHECK
此类型的互斥锁可提供错误检查。
如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则会返回错误。
如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。
如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
PTHREAD_MUTEX_RECURSIVE
如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则可成功锁定该互斥锁。
与PTHREAD_MUTEX_NORMAL类型的互斥锁不同,对此类型互斥锁进行重新锁定时不会产生死锁情况。
多次锁定互斥锁需要进行相同次数的解除锁定才可以释放该锁,然后其他线程才能获取该互斥锁。
如果线程尝试解除锁定的互斥锁未锁定,则会返回错误
PTHREAD_MUTEX_DEFAULT
如果尝试以递归方式锁定此类型的互斥锁,则会产生不确定的行为。
对于不是由调用线程锁定的此类型互斥锁,如果尝试对它解除锁定,则会产生不确定的行为。
对于尚未锁定的此类型互斥锁,如果尝试对它解除锁定,也会产生不确定的行为。
允许在实现中将该互斥锁映射到其他互斥锁类型之一
应用互斥量需要注意的几点
1)互斥量需要时间来进行加锁和解锁。
锁住较少互斥量的程序通常运行更快。
所以互斥量应尽量少,够用即可,每个互斥量保护的区域应则尽量大。
2)互斥量的本质是串行执行,如果很多线程需要频繁的加锁同一个互斥量,则线程的大部分时间就会在等待,这对性能是有害的,如果互斥量保护的数据(或代码)保护彼此无关的片段,则可以把特大的互斥量分解为几个小互斥量来提高性能,这样任意时刻需要小互斥量的线程就会减少,线程等待时间就会减少,所以互斥量应该足够多,每个互斥量保护的区域应该足够少。
第四节条件变量
互斥锁一个明显的缺点是它只有两种状态:
锁定和非锁定。
而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。
它常和互斥锁一起使用。
使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。
一旦其他某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。
这些线程将重新锁定互斥锁并重新测试条件是否满足。
一般来说,条件变量被用来进行线程间的同步。
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
一个线程等待”条件变量的条件成立”而挂起,另一个线程使”条件成立”(给出条件成立信号)。
为了防止竞争,防止多个线程同时请求pthread_cond_wait()或者pthread_cond_timedwait()的竞争条件,条件变量的使用总是和一个互斥锁结合在一起。
没有条件变量,开发者需要线程不停的轮询以查询条件是否满足,因为线程要不停的忙等,会消耗很多资源。
条件变量就是不用轮询而能达到同样目的方法
相关的函数如下:
int
pthread_cond_init(pthread_cond_t
*cond,pthread_condattr_t
*cond_attr);
pthread_cond_wait(pthread_cond_t
*cond,pthread_mutex_t
*mutex);
pthread_cond_timewait(pthread_cond_t
*cond,pthread_mutex
*mutex,const
timespec
*abstime);
pthread_cond_destroy(pthread_cond_t
*cond);
pthread_cond_signal(pthread_cond_t
pthread_cond_broadcast(pthread_cond_t
//解除所有线程的阻塞
条件变量采用的数据类型是pthread_cond_t,在使用之前必须要进行初始化,这包括两种方式:
静态:
可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量.
pthread_cond_tmyCond=PTHREAD_COND_INITIALIZER;
(用于进程间线程的通信)。
动态:
pthread_cond_init函数,在释放动态条件变量的内存空间之前,要用pthread_cond_destroy对其进行清理.
intpthread_cond_init(pthread_cond_t*cond,constpthread_condattr_t*attr);
其中cond是一个指向结构pthread_cond_t的指针
cond_attr是一个指向结构pthread_condattr的指针,如果attr为NULL,那么它将使用缺省的属性来设置所指定的条件变量。
结构pthread_condatt_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用。
默认值是PTHREAD_PROCESS_PRIVATE,即此条件变量被同一进程内的各线程使用。
释放一个条件变量函数为intpthread_cond_destroy(pthread_cond_t*cond);
函数pthread_cond_wait()使线程阻塞在一个条件变量上。
intpthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex);
线程解开mutex指向的锁并被条件变量cond阻塞。
线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒。
但是要注意的是,条件变量只是其阻塞和唤醒线程的作用,具体判断条件还需用户给出。
线程被唤醒后,它将重新检查条件是否满足,如果不满足,一般来说线程应该仍阻塞在这里,被等待下一次唤醒。
这个过程一般用while语句实现
另一个用来阻塞线程的函数是
intpthread_cond_timedwait(pthread_cond_t*cond,pthread_mutex_tmytex,conststructtimespec*abstime);
它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也会解除
intpthread_co
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- UNIX 多线程 学习 笔记