自己对多线程编程同步方面,从新梳理,也算复习了。^_^
经典多线程同步问题:
程序描述:
主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) ->全局变量++ -> sleep(0) -> 输出参数和全局变量。
要求:
1.子线程输出的线程序号不能重复。
2.全局变量的输出必须递增。
首先拿到这个问题,看到全局变量的递增,就想到了关键代码段,互斥量,事件等方法。自己挑了个互斥量的方法来写,但在自己写完调试发现,要求1:子线程输出的序号不能重复,写了互斥量方法居然不成功。这就蛋疼了~~~贴上互斥量的代码
#include <stdio.h>
#include <process.h>
#include <Windows.h>
long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);
HANDLE g_hMutex = NULL;
int main()
{
g_lngNum = 0;
HANDLE handle[THREAD_NUM];
g_hMutex = CreateMutex(NULL, FALSE, L"Mutex");
for (int i = 0; i < THREAD_NUM; ++i)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hMutex, INFINITE);
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
CloseHandle(g_hMutex);
for (int i = 0; i < THREAD_NUM; i++)
{
CloseHandle(handle[i]);
}
return 0;
}
unsigned int _stdcall Fun( void* pPM )
{
int nThreadNum = *(int*)pPM;
ReleaseMutex(g_hMutex);
Sleep(50);
g_lngNum++;
Sleep(0);
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_lngNum);
return 0;
}
输出结果:
线程编号为10 全局资源值为1
线程编号为10 全局资源值为2
线程编号为10 全局资源值为3
.....
全局资源互斥的递增,但是线程编号会出现重复。。。。
在调试的时候发现,在for里面和子线程里面打断点 调试时候并不会交替执行,而是一直在for里面执行,这感到纳闷了
WaitForSingleObject等待到信号,会将该计数器置为1 下次再等待的时候g_hMutex肯定没信号啊,那for怎么能一直执行。
后面发现了,互斥量的一个特性:线程所有权。在主线程for循环创建线程时候,等待到了互斥量有信号,相当于主线程拿到了通行证,等到第二次for时候 在wait检测的时候,发现主线程有通行证 就放行了....
在网上查阅了相关资料后发现,关键段代码、互斥量都有线程所有权的概念,解决这个同步问题,可用事件和信号量来解决。。。。。。
事件的代码
#include <stdio.h>
#include <process.h>
#include <Windows.h>
long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);
HANDLE g_hEvent = NULL;
int main()
{
g_lngNum = 0;
HANDLE handle[THREAD_NUM];
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
for (int i = 0; i < THREAD_NUM; ++i)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hEvent, INFINITE);
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
CloseHandle(g_hEvent);
for (int i = 0; i < THREAD_NUM; i++)
{
CloseHandle(handle[i]);
}
return 0;
}
unsigned int _stdcall Fun( void* pPM )
{
int nThreadNum = *(int*)pPM;
SetEvent(g_hEvent);
Sleep(50);
g_lngNum++;
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_lngNum);
return 0;
}
才用事件方式,经典线线程同步问题已经圆满的解决了——线程编号的输出没有重复,说明主线程与子线程达到了同步。全局资源的输出是递增的,说明各子线程已经互斥的访问和输出该全局资源。
顺便贴上关键段代码和信号量代码
关键段代码:
#include <stdio.h>
#include <process.h>
#include <Windows.h>
long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);
CRITICAL_SECTION g_csThreadp, g_csThreadCode;
int main()
{
g_lngNum = 0;
HANDLE handle[THREAD_NUM];
InitializeCriticalSection(&g_csThreadp);
InitializeCriticalSection(&g_csThreadCode);
for (int i = 0; i < THREAD_NUM; ++i)
{
EnterCriticalSection(&g_csThreadp);
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
for (int i = 0; i < THREAD_NUM; i++)
{
CloseHandle(handle[i]);
}
DeleteCriticalSection(&g_csThreadp);
DeleteCriticalSection(&g_csThreadCode);
return 0;
}
unsigned int _stdcall Fun( void* pPM )
{
int nThreadNum = *(int*)pPM;
LeaveCriticalSection(&g_csThreadp);
Sleep(50);
EnterCriticalSection(&g_csThreadCode);
g_lngNum++;
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_lngNum);
LeaveCriticalSection(&g_csThreadCode);
return 0;
}
信号量代码
#include <stdio.h>
#include <process.h>
#include <Windows.h>
long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);
HANDLE g_hThreadParameter;
int main()
{
g_lngNum = 0;
HANDLE handle[THREAD_NUM];
g_hThreadParameter = CreateSemaphore(NULL, 0, 1, NULL); //创建一个初始资源为0,最大并发数为1的信号量
for (int i = 0; i < THREAD_NUM; ++i)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadParameter, INFINITE); // 信号量资源大于0触发,触发将资源减一
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
CloseHandle(g_hThreadParameter);
for (int i = 0; i < THREAD_NUM; i++)
{
CloseHandle(handle[i]);
}
return 0;
}
unsigned int _stdcall Fun( void* pPM )
{
int nThreadNum = *(int*)pPM;
ReleaseSemaphore(g_hThreadParameter, 1, NULL); //信号量++
Sleep(50);
g_lngNum++;
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_lngNum);
return 0;
}