文档详情

4:一个经典的多线程同步问题.doc

发布:2016-08-15约1.72万字共22页下载文档
文本预览下载声明
一个经典的多线程同步问题 程序描述: 主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 - sleep(50) - 全局变量++ - sleep(0) - 输出参数和全局变量。 要求: 1.子线程输出的线程序号不能重复。 2.全局变量的输出必须递增。 下面画了个简单的示意图: 分析下这个问题的考察点,主要考察点有二个: 1.主线程创建子线程并传入一个指向变量地址的指针作参数,由于线程启动须要花费一定的时间,所以在子线程根据这个指针访问并保存数据前,主线程应等待子线程保存完毕后才能改动该参数并启动下一个线程。这涉及到主线程与子线程之间的同步。 2.子线程之间会互斥的改动和输出全局变量。要求全局变量的输出必须递增。这涉及到各子线程间的互斥。 ? 下面列出这个程序的基本框架,可以在此代码基础上进行修改和验证。 //经典线程同步互斥问题 #include stdio.h #include process.h #include windows.h long g_nNum; //全局资源 unsigned int __stdcall Fun(void *pPM); //线程函数 const int THREAD_NUM = 10; //子线程个数 int main() { g_nNum = 0; HANDLE handle[THREAD_NUM]; int i = 0; while (i THREAD_NUM) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, i, 0, NULL); i++;//等子线程接收到参数时主线程可能改变了这个i的值 } //保证子线程已全部运行结束 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); return 0; } unsigned int __stdcall Fun(void *pPM) { //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来 int nThreadNum = *(int *)pPM; //子线程获取参数 Sleep(50);//some work should to do g_nNum++; //处理全局资源 Sleep(0);//some work should to do printf(线程编号为%d 全局资源值为%d\n, nThreadNum, g_nNum); return 0; } 运行结果: 可以看出,运行结果完全是混乱和不可预知的。运用Windows平台下各种手段包括关键段,事件,互斥量,信号量等等来解决这个问题。 关键段CRITICAL_SECTION首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理。 关键段CRITICAL_SECTION一共就四个函数,使用很是方便。下面是这四个函数的原型和使用说明。 ? 函数功能:初始化 函数原型: void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 函数说明:定义关键段变量后必须先初始化。 ? 函数功能:销毁 函数原型: void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 函数说明:用完之后记得销毁。 ? 函数功能:进入关键区域 函数原型: void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 函数说明:系统保证各线程互斥的进入关键区域。 ? 函数功能:离开关关键区域 函数原型: void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); ? 然后在经典多线程问题中设置二个关键区域。一个是主线程在递增子线程序号时,另一个是各子线程互斥的访问输出全局资源时。详见代码: #include stdio.h #include process.h #include windows.h long g_nNum; unsigned int __stdcall Fun(void *pPM); const int THREAD_NUM = 10; //关键段变量声明 CRITICAL_SECTION g_csThreadParameter, g_csThreadCode; int main() { printf(经典线程同步--关
显示全部
相似文档