Win32 线程知识点梳理二
2015-09-15 22:13
246 查看
Win32中sendMessage() 是同步行为,而PostMessage()是异步行为。关于进程和线程的协调工作是由同步机制来完成的。
注意:Critical Section不是核心对象,没有所谓的handle这样的东西,它存在于进程的内存空间,你不需要使用Create这样的API函数来获得一个critical section handle。
你应该做的是对一个类型为CRITICAL_SECTION的变量初始化。调用InitializeCriticalSection
参数1:1个指针,指向欲被初始化的critical_section变量
参数1:指向一个不再需要的CRITICAL_SECTION变量
参数1:指向一个即将锁定的变量
当线程即将进入其中,必须通过这一关。
参数1: 指向一个即将解除锁定的变量
警告:不要在一个critical section之中调用sleep()或任何Wait…()API函数
由于critical section不是核心对象,如果进入critical section的那个线程挂掉而没有调用LeaveCriticalSection()的话,系统没有办法将该critical section清除。如果需要这样的机能,应该使用mutex。
锁住一个未被拥有的mutex,比锁住一个未被拥有的critical section需要花费几乎100倍的时间,因为 critical
section只是在user mode下就可以完成。 Mutexes可以跨进程使用,Critical section只能在同一个进程中使用
等待一个Mutex时,你可以指定结束等待的时间长度,而等待一个Critical Section不行。
参数1:安全属性,NULL表示使用默认属性
参数2:调用CreateMutex这个函数的线程同时拥有mutex,该值设定为true
参数3:mutex的名称,名称是字符串,任何进程和线程都可以使用这个名称
返回值: 成功返回一个handle,否则返回NULL,可以通过调用GetLastError()来获得失败信息,如,指定的mutex名称已经存在,则会传回
参数2:If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
如果其他线程以WaitForMultipleObjects()等待此mutex,该函数也会返回,传回值介于:
如果我们修改程序,让哲学家一次等待两只筷子,程序代码像这样:
也就是哲学家只有在有一对筷子时才会拿起筷子,死锁就不会发生。
修正上面的代码
参数1:安全属性
参数2:semaphore初值,必须大于或等于0,小于等于lMaximumCount
参数3:Semaphore最大值,也就是同一个时间能够锁住Semaphore的最多个数
参数4: Semaphore名称,任何线程都可以根据这一名称引用到这个Semaphore, NULL表示没有名字
参数1:Semaphore的handle
参数2:Semaphore现值的增额,不可以为负值或0
参数3:传回Semaphore原来的现值,是一个瞬间值,不可以把IReleaseCount+*lpPreviousCount作为semaphore的现值,因为别的线程可能已经改变了semaphore的值。
Event对象被应用在多种类型的高级I/O操作中。
参数1:安全属性
参数2:如果为FALSE,表示这个event将在变为激发态之后自动变为非激发态,如果为True表示不会自动重置,必须依靠程序调用ResetEvent()才能变为非激发态
参数3:如果为true,表示这个event一开始处于激发态,如果为false,表示一开始处于非激发态
参数4:event对象的名字
把对象设置为激发状态
把对象设定为非激发状态
如果是一个manual reset event,把event对象设为激发状态,唤醒
如果对一个autoReset event对象调用setEvent()或PulseEvent(),而彼时没有任何线程在等待,event会被遗失,换句话说,除非有线程正在等待,否则event不会被保存下来。
这里就可能存在着一种死锁的情况:
receiver线程检查队列是否有字符,这时发生context switch,切换到sender线程,它对一个event对象进行pulse操作,这时候又发生了context switch,回到receiver线程,调用waitForSingleObject(),等待event对象,由于这个动作发生在sender线程激发event之后,所以event会遗失—— 所以receiver永远不会醒来,程序就进入了死锁的状态。
其实是一个原子操作,如:
Decrements (decreases by one) the value of the specified 32-bit variable as an atomic operation.
参数1:32位变量的地址,这个变量被递减,然后将结果与0比较,这个地址必须指向long word
如果等于0,返回0,大于0传回正值,小于0传回负值。
interlocked..()如果被用于spin-lock(又称为自旋锁,线程通过busy-wait-loop的方式来获取锁,任何时刻时刻只有一个线程能够获得锁,其他线程忙等待直到获得锁,spinlock不会导致线程的状态切换)那么它只是一种同步机制。
它主要用于引用计数,可以不需要用到critical section或mutex之类,毕竟一个32位变量的存取操作只需要2~3个机器指令而已。
资料来源:
Win32多线程程序设计
Critical Section 临界区
它的含义是指一小块用来处理一份被共享的资源的程序代码,这里的资源指的是广义的如一块内存、一个数据结构、一个文件或其他具有使用排他性的的东西。对资源的保护是通过允许一次仅仅一个线程进入critical section。
注意:Critical Section不是核心对象,没有所谓的handle这样的东西,它存在于进程的内存空间,你不需要使用Create这样的API函数来获得一个critical section handle。
你应该做的是对一个类型为CRITICAL_SECTION的变量初始化。调用InitializeCriticalSection
void WINAPI InitializeCriticalSection( _Out_ LPCRITICAL_SECTION lpCriticalSection );
参数1:1个指针,指向欲被初始化的critical_section变量
void WINAPI DeleteCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection );
参数1:指向一个不再需要的CRITICAL_SECTION变量
void WINAPI EnterCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection );
参数1:指向一个即将锁定的变量
当线程即将进入其中,必须通过这一关。
void WINAPI LeaveCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection );
参数1: 指向一个即将解除锁定的变量
警告:不要在一个critical section之中调用sleep()或任何Wait…()API函数
由于critical section不是核心对象,如果进入critical section的那个线程挂掉而没有调用LeaveCriticalSection()的话,系统没有办法将该critical section清除。如果需要这样的机能,应该使用mutex。
DeadLock/ The Deadly Embrace
当双方都握有对方所需要的东西,这种情况称为死锁。void SwapLists(List* list, List* list2){ List * tmp_list; EnterCriticalSection(list1->critical_sec); EnterCriticalSection(list2->critical_sec); tmp->list = list1->head; list1->head = list2->head; list2->head = tmp->list; LeaveCriticalSection(list1->critical_sec); LeaveCriticalSection(list2->critical_sec); }
Mutex
首先谈下Mutex和CriticalSection的区别:锁住一个未被拥有的mutex,比锁住一个未被拥有的critical section需要花费几乎100倍的时间,因为 critical
section只是在user mode下就可以完成。 Mutexes可以跨进程使用,Critical section只能在同一个进程中使用
等待一个Mutex时,你可以指定结束等待的时间长度,而等待一个Critical Section不行。
产生一个互斥器
HANDLE WINAPI CreateMutex( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, _In_ BOOL bInitialOwner, _In_opt_ LPCTSTR lpName );
参数1:安全属性,NULL表示使用默认属性
参数2:调用CreateMutex这个函数的线程同时拥有mutex,该值设定为true
参数3:mutex的名称,名称是字符串,任何进程和线程都可以使用这个名称
返回值: 成功返回一个handle,否则返回NULL,可以通过调用GetLastError()来获得失败信息,如,指定的mutex名称已经存在,则会传回
ERROR_ALREADY_EXISTS
打开一个互斥器
如果一个mutex已经产生并有一个名称,那么其它的进程和线程就可以根据该名称打开那个mutex。HANDLE WINAPI OpenMutex( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName );
参数2:If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
关于激发态:
一旦没有任何线程拥有mutex,并且有一个线程以Wait…()等待该mutex,该mutex就会短暂出现激发状态,使得Wait…()得以返回,随后Mutex又会立即变为非激发状态,知道获得该Mutex的线程调用ReleaseMutex()将该mutex释放掉。被舍弃的Mutex
如果一个线程拥有一个mutex,在结束前却没有调用ReleaseMutex,mutex不会被摧毁,而下一个等待的线程会被以WAIT_ABANDONED_0通知,不论线性是因为ExitThread()而结束或是因为挂掉而结束,这种情况都会存在。
如果其他线程以WaitForMultipleObjects()等待此mutex,该函数也会返回,传回值介于:
WAIT_ABANDONED_0 ~ WAIT_ABANDONED_0+N-1
关于死锁
在哲学家进餐问题中,他们不愿意在吃完之前放下他们的筷子,但是一定要一双筷子才可以开始吃,如果允许哲学家一次取得一只筷子,那么就有可能每个哲学家都抓住了左手的筷子,这时他们就不可能抓住右手的筷子,因为右手边的哲学家拒绝让出。如果我们修改程序,让哲学家一次等待两只筷子,程序代码像这样:
WaitForMultipleObjects(2,myChopsticks,TRUE,INFINITE);
也就是哲学家只有在有一对筷子时才会拿起筷子,死锁就不会发生。
修正上面的代码
void swapLists(struct List * list ,struct List * list2){ struct List * tmp_list; HANDLE arrhandles[2]; arrhandles[0] = list1->hMutex; arrhandles[1] = list2->hMutex; WaitForMultipleObjects(2,arrhandles,TRUE,INFINITE); tmp->list = list1->head; list1->head = list2->head; ReleaseMutex(arrhandles[0]); ReleaseMutex(arrhandles[1]); }
Semaphores
mutex是semaphore的一种退化,如果你产生一个semaphore,并令最大值为1,那就是一个mutex,mutex因此又被称为binary semaphore。产生信号量
HANDLE WINAPI CreateSemaphore( _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, _In_ LONG lInitialCount, _In_ LONG lMaximumCount, _In_opt_ LPCTSTR lpName );
参数1:安全属性
参数2:semaphore初值,必须大于或等于0,小于等于lMaximumCount
参数3:Semaphore最大值,也就是同一个时间能够锁住Semaphore的最多个数
参数4: Semaphore名称,任何线程都可以根据这一名称引用到这个Semaphore, NULL表示没有名字
获得锁定
每当一个锁定动作(调用Wait…())成功,semaphore现值就会减少1,如果锁定成功你也不会拥有它的所有权。解除锁定
BOOL WINAPI ReleaseSemaphore( _In_ HANDLE hSemaphore, _In_ LONG lReleaseCount, _Out_opt_ LPLONG lpPreviousCount );
参数1:Semaphore的handle
参数2:Semaphore现值的增额,不可以为负值或0
参数3:传回Semaphore原来的现值,是一个瞬间值,不可以把IReleaseCount+*lpPreviousCount作为semaphore的现值,因为别的线程可能已经改变了semaphore的值。
事件(Event Objects)
Win32中最具弹性的同步机制就是events对象,Event对象是一种核心对象,它通过程序来控制成为激发状态和未激发状态。Event对象被应用在多种类型的高级I/O操作中。
产生一个event对象
HANDLE WINAPI CreateEvent( _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, _In_ BOOL bManualReset, _In_ BOOL bInitialState, _In_opt_ LPCTSTR lpName );
参数1:安全属性
参数2:如果为FALSE,表示这个event将在变为激发态之后自动变为非激发态,如果为True表示不会自动重置,必须依靠程序调用ResetEvent()才能变为非激发态
参数3:如果为true,表示这个event一开始处于激发态,如果为false,表示一开始处于非激发态
参数4:event对象的名字
BOOL WINAPI SetEvent( _In_ HANDLE hEvent );
把对象设置为激发状态
BOOL WINAPI ResetEvent( _In_ HANDLE hEvent );
把对象设定为非激发状态
BOOL WINAPI PulseEvent( _In_ HANDLE hEvent );
如果是一个manual reset event,把event对象设为激发状态,唤醒
所有等待中的线程,然后把event恢复为非激发状态,如果是一个auto reset event,把event对象设为激发状态,唤醒
一个等待中的线程,然后event恢复为非激发状态。
如果对一个autoReset event对象调用setEvent()或PulseEvent(),而彼时没有任何线程在等待,event会被遗失,换句话说,除非有线程正在等待,否则event不会被保存下来。
这里就可能存在着一种死锁的情况:
receiver线程检查队列是否有字符,这时发生context switch,切换到sender线程,它对一个event对象进行pulse操作,这时候又发生了context switch,回到receiver线程,调用waitForSingleObject(),等待event对象,由于这个动作发生在sender线程激发event之后,所以event会遗失—— 所以receiver永远不会醒来,程序就进入了死锁的状态。
Interlocked Variables
这是同步机制最简单的类型,对标准的32位变量进行操作,它们只保证对某个特定变量的存取操作是一个个按顺序来的。其实是一个原子操作,如:
LONG __cdecl InterlockedIncrement( _Inout_ LONG volatile *Addend );
Decrements (decreases by one) the value of the specified 32-bit variable as an atomic operation.
LONG __cdecl InterlockedDecrement( _Inout_ LONG volatile *Addend );
参数1:32位变量的地址,这个变量被递减,然后将结果与0比较,这个地址必须指向long word
如果等于0,返回0,大于0传回正值,小于0传回负值。
LONG __cdecl InterlockedExchange( _Inout_ LONG volatile *Target, _In_ LONG Value );
interlocked..()如果被用于spin-lock(又称为自旋锁,线程通过busy-wait-loop的方式来获取锁,任何时刻时刻只有一个线程能够获得锁,其他线程忙等待直到获得锁,spinlock不会导致线程的状态切换)那么它只是一种同步机制。
它主要用于引用计数,可以不需要用到critical section或mutex之类,毕竟一个32位变量的存取操作只需要2~3个机器指令而已。
资料来源:
Win32多线程程序设计
相关文章推荐
- 核心动画1、(初解)
- HDOJ 1877 又一版 A+B(进制转换,水)
- Flume前述(二)--功能配置
- Cascade Adaboost级联分类器的训练
- Cascade Adaboost级联分类器的训练
- HDU 5441 Travel (带权并查集 + 离线操作)
- select模型
- Oracle 分区表相关信息
- hdu 1251 简单字典树
- 数据结构面试题1.2.6-Top K算法详细解析——百度面试题
- 使用Htmlhelper,创建文本框TextBox
- 苹果MAC系统下JAVA_HOME环境变量配置
- Objective-C 基础知识之(十): OC中的排序方法
- 收集的一个响应式基于bootstrap3的Color Admin 1.7后台模板
- 解决浮点数自动取整问题
- 赵文成 VMware Workstation nat 借助宿主机上网 (移动虚拟机需要重新添加vmdk文件)
- UVALive 6659 Dromicpalin Substrings
- 解决浮点数自动取整问题
- POJ1151Atlantis【离散化+扫描线+线段树】
- iOS学习 用代码实现界面