多线程第三篇:关键段(临界区)
2013-12-15 19:01
302 查看
线程之间同步于互斥:
我们先来做一道题:
问题描述:
1.主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。
2.子线程接收参数 -> sleep(50) -> 全局变量++
-> sleep(0) -> 输出参数和全局变量。
要求:
1.子线程输出的线程序号不能重复。
2.全局变量的输出必须递增。
对程序的第一步改进:
好了让我们看一看结果:(惨不忍睹呀!!!)
程序到底做了什么,这是一坨神马玩意????!!!!!!!!oh,no.
我们来分析一下到底为什么产生这种结果:
1.我们没有对输出进行原子控制,即打印语句不可中断.
2.我们没有控制线程的执行顺序,线程每次总是随机执行,并且执行期间可能被中断.
下面关键段出场了.
关键段一共四个函数:
1.初始化函数:(在使用关键段前必须初始化)
void InitializeCriticalSection (LPCRITICAL_SECTION lpCriticalSection);
2.销毁关键段:(使用完关键段后必须销毁,以免造成资源泄露)
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection);
3.进入关键区:(保证各个线程之间互斥进入,但是主线程例外,主线程可以随时进入关键段)
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
我们来编一段代码,使用关键段:
结果:
终于输出顺序输出1-10了,但是我们的线程编号为什么都是10呀,
这里我们解释一下:
1.关键段控制线程互斥的,主线程可以随时随地进入关键段,不受限制.
2.主线程创建线程和执行线程是分开的.即先创建,可能立即执行也肯能一段时间后在执行,这个是随机的.
3.不同关键段之间是不互斥的,如果线程A正在关键段M中执行,忽然cpu调度,执行线程B,此时线程B是不可以进入关键段M的,但是线程B可以进入除了关键段M之外的任意部分,如线程B进入了关键段N,或者线程B没进入关键段,只是执行没在关键段之中的代码.
上面有一处小小的不足,不知道大家看出来没.
就是main函数中的EnterCriticalSection .他并没起任何作用,我们用程序来验证一下:
我们可以清楚的看到,EnterCriticalSection (&g_csThreadParameter )并没有使下面这段程序互斥,所以我们一定要切记,控制线程的关键段(临界区)一定要在线程函数内部使用.
我们再进一步改进代码:
我们更深入的探讨一下临界区应该怎么用.
我们再来看一段代码:
[align=left]结果:[/align]
[align=left]代码很好理解,用event控制线程producer先执行,consumer后执行.[/align]
[align=left]但是代码在producer中进入临界区,在consumer离开临界区,证明了,临界区并没有谁进入谁离开的限制,即在线程a进入在线程b离开也是可以的.[/align]
我们先来做一道题:
问题描述:
1.主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。
2.子线程接收参数 -> sleep(50) -> 全局变量++
-> sleep(0) -> 输出参数和全局变量。
要求:
1.子线程输出的线程序号不能重复。
2.全局变量的输出必须递增。
对程序的第一步改进:
#include <iostream> #include <windows.h> #include <process.h> int g_count = 0; unsigned int __stdcall ThreadFun(void *pthread_num) { Sleep(50); int num = *(( int*)pthread_num ); std::cout<< "线程编号为:" <<num<< "全局资源编号为:" <<++g_count<< std::endl ; Sleep(0); return 0; } int main () { const int thread_num = 10; HANDLE handle[thread_num ]; for ( int i = 0 ; i<thread_num ; ++i ){ handle[i ] = (HANDLE) _beginthreadex(NULL ,0,ThreadFun,& i,0,NULL ); } //等待所有创建的子线程都执行完. WaitForMultipleObjects( thread_num,handle ,true,INFINITE); return 0; }
好了让我们看一看结果:(惨不忍睹呀!!!)
程序到底做了什么,这是一坨神马玩意????!!!!!!!!oh,no.
我们来分析一下到底为什么产生这种结果:
1.我们没有对输出进行原子控制,即打印语句不可中断.
2.我们没有控制线程的执行顺序,线程每次总是随机执行,并且执行期间可能被中断.
下面关键段出场了.
关键段一共四个函数:
1.初始化函数:(在使用关键段前必须初始化)
void InitializeCriticalSection (LPCRITICAL_SECTION lpCriticalSection);
2.销毁关键段:(使用完关键段后必须销毁,以免造成资源泄露)
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection);
3.进入关键区:(保证各个线程之间互斥进入,但是主线程例外,主线程可以随时进入关键段)
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
我们来编一段代码,使用关键段:
#include <iostream> #include <windows.h> #include <process.h> int g_count = 0; CRITICAL_SECTION g_csThreadParameter, g_csThreadCode; unsigned int __stdcall ThreadFun(void *pthread_num) { Sleep(50); int num = *(( int*)pthread_num ); LeaveCriticalSection(& g_csThreadParameter); EnterCriticalSection(& g_csThreadCode); std::cout<< "线程号:" <<GetCurrentThreadId()<< "线程编号为地址:" <<&num<< "线程编号" <<num<< "全局资源编号为:" <<++g_count<< std::endl ; LeaveCriticalSection(& g_csThreadCode); Sleep(0); return 0; } int main () { InitializeCriticalSection(& g_csThreadParameter); InitializeCriticalSection(& g_csThreadCode); const int thread_num = 10; HANDLE handle[thread_num ]; for ( int i = 0 ; i<thread_num ; ++i ){ EnterCriticalSection(&g_csThreadParameter ); handle[i ] = (HANDLE) _beginthreadex(NULL ,0,ThreadFun,& i,0,NULL ); } //等待所有创建的子线程都执行完. WaitForMultipleObjects( thread_num,handle ,true,INFINITE); return 0; }
结果:
终于输出顺序输出1-10了,但是我们的线程编号为什么都是10呀,
这里我们解释一下:
1.关键段控制线程互斥的,主线程可以随时随地进入关键段,不受限制.
2.主线程创建线程和执行线程是分开的.即先创建,可能立即执行也肯能一段时间后在执行,这个是随机的.
3.不同关键段之间是不互斥的,如果线程A正在关键段M中执行,忽然cpu调度,执行线程B,此时线程B是不可以进入关键段M的,但是线程B可以进入除了关键段M之外的任意部分,如线程B进入了关键段N,或者线程B没进入关键段,只是执行没在关键段之中的代码.
上面有一处小小的不足,不知道大家看出来没.
就是main函数中的EnterCriticalSection .他并没起任何作用,我们用程序来验证一下:
#include <iostream> #include <windows.h> #include <process.h> int g_count = 0; CRITICAL_SECTION g_csThreadParameter, g_csThreadCode; unsigned int __stdcall ThreadFun(void *pthread_num) { Sleep(50); //EnterCriticalSection(&g_csThreadParameter); int num = *(( int*)pthread_num ); std::cout<< "线程号:" <<GetCurrentThreadId()<< "线程编号" <<num<< std::endl ; std::cout<<111<< std::endl ; LeaveCriticalSection(& g_csThreadParameter); EnterCriticalSection(& g_csThreadCode); std::cout<< "线程号:" <<GetCurrentThreadId()<< "线程编号为地址:" <<&num<< "线程编号" <<num<< "全局资源编号为:" <<++g_count<< std::endl ; LeaveCriticalSection(& g_csThreadCode); Sleep(0); return 0; } int main () { InitializeCriticalSection(& g_csThreadParameter); InitializeCriticalSection(& g_csThreadCode); const int thread_num = 10; HANDLE handle[thread_num ]; for ( int i = 0 ; i<thread_num ; ++i ){ EnterCriticalSection(&g_csThreadParameter ); handle[i ] = (HANDLE) _beginthreadex(NULL ,0,ThreadFun,& i,0,NULL ); } //等待所有创建的子线程都执行完. WaitForMultipleObjects( thread_num,handle ,true,INFINITE); return 0; }
我们可以清楚的看到,EnterCriticalSection (&g_csThreadParameter )并没有使下面这段程序互斥,所以我们一定要切记,控制线程的关键段(临界区)一定要在线程函数内部使用.
Sleep(50); //EnterCriticalSection(&g_csThreadParameter); int num = *(( int *)pthread_num ); std:: cout<< "线程号:" << GetCurrentThreadId()<< "线程编号" << num<< std ::endl ; std:: cout<<111<< std ::endl ; LeaveCriticalSection(& g_csThreadParameter );
我们再进一步改进代码:
#include <iostream> #include <windows.h> #include <process.h> int g_count = 0; CRITICAL_SECTION g_csThreadParameter, g_csThreadCode; unsigned int __stdcall ThreadFun(void *pthread_num) { Sleep(50); EnterCriticalSection(& g_csThreadParameter); int num = *(( int*)pthread_num ); std::cout<< "线程号:" <<GetCurrentThreadId()<< "线程编号" <<num<< std::endl ; std::cout<<111<< std::endl ; LeaveCriticalSection(& g_csThreadParameter); EnterCriticalSection(& g_csThreadCode); std::cout<< "线程号:" <<GetCurrentThreadId()<< "线程编号为地址:" <<&num<< "线程编号" <<num<< "全局资源编号为:" <<++g_count<< std::endl ; LeaveCriticalSection(& g_csThreadCode); Sleep(0); return 0; } int main () { InitializeCriticalSection(& g_csThreadParameter); InitializeCriticalSection(& g_csThreadCode); const int thread_num = 10; HANDLE handle[thread_num ]; for ( int i = 0 ; i<thread_num ; ++i ){ // EnterCriticalSection(&g_csThreadParameter); handle[i ] = (HANDLE) _beginthreadex(NULL ,0,ThreadFun,& i,0,NULL ); } //等待所有创建的子线程都执行完. WaitForMultipleObjects( thread_num,handle ,true,INFINITE); return 0; }
我们更深入的探讨一下临界区应该怎么用.
我们再来看一段代码:
#include <iostream> #include <windows.h> #include <process.h> int g_count = 0; CRITICAL_SECTION producer_critical, consumer_critical ; HANDLE producer_semaphore , consumer_semaphore; CRITICAL_SECTION all; HANDLE hevent ; unsigned int __stdcall producer (void *) { EnterCriticalSection(& all ); std ::cout <<111<< std:: endl; SetEvent( hevent ); return 0; } unsigned int __stdcall consumer (void *) { WaitForSingleObject( hevent ,INFINITE ); LeaveCriticalSection(& all ); EnterCriticalSection(& all ); std ::cout <<222<< std:: endl; return 0; } int main () { InitializeCriticalSection(& all ); hevent = CreateEvent (NULL , false, false, NULL); HANDLE hproducer = (HANDLE ) _beginthreadex( NULL ,0,producer , NULL,0, NULL ); HANDLE hconsumer = (HANDLE ) _beginthreadex( NULL ,0,consumer , NULL,0, NULL ); WaitForSingleObject( hproducer ,INFINITE ); WaitForSingleObject( hconsumer ,INFINITE ); CloseHandle( hproducer ); CloseHandle( hconsumer ); return 0; }
[align=left]结果:[/align]
[align=left]代码很好理解,用event控制线程producer先执行,consumer后执行.[/align]
[align=left]但是代码在producer中进入临界区,在consumer离开临界区,证明了,临界区并没有谁进入谁离开的限制,即在线程a进入在线程b离开也是可以的.[/align]
相关文章推荐
- 集成libevent,google protobuf的RPC框架
- LeetCode | Search in Rotated Sorted Array II
- 数据库执行sql报错Got a packet bigger than 'max_allowed_packet' bytes及重启mysql
- jQuery EasyUI
- 第一篇记录:冒泡排序
- C++学习方法
- unity地形Terrain浅谈
- 谈谈扩充式扩展与增量式扩展
- JQuery知识快览之五—操作属性和结构
- 再谈我是怎么招聘程序员的
- java 获取非堆内存使用量
- 在Lu脚本中绘制分裂视图
- 成为Java高手的25个学习目标
- 在Lu脚本中用Plot绘制普通XY图
- 无缝世界网游服务器架构的设计思路(续)
- 戒除浮躁,重新上路,脚踏实地——致我的嵌入式新征程
- K-means算法
- VC6.0 Dialog 如何添加 WM_ERASEBACKGROUND 消息
- xdebug
- 在Lu脚本中绘制普通XY图(折线图)