Win32线程同步
一、线程同步
1 多线程的问题
A停止 -> B开始 -〉B停止 -> A开始
当线程停止会保存寄存器的状态。
当线程开始会恢复寄存器的状态。
AB线程都使用printf的问题:
A线程调用printf时,printf正在输出
当中,A挂起,B执行,B线程也调用
printf输出B的数据,画面会出现A的
数据输出1部分,然后是B的数据;
B挂起,A执行, A继续输出自己的数据.
所以,由于多线程的切换,产生数据混乱.
2 问题的解决 - 同步机制
2.1 原子锁
2.2 临界区
2.3 事件
2.4 互斥
2.5 信号量
2.6 可等候定时器
3 等候多个内核对象事件
DWORD WaitForMultipleObjects(
DWORD nCount,//句柄的数量
CONST HANDLE *lpHandles,//句柄数组
BOOL fWaitAll, //等候方式
DWORD dwMilliseconds );//等候时间
等候方式fWaitAll:
TRUE - 每个句柄都有事件,解除阻塞
FALSE - 其中一个句柄有事件,解除阻塞
二、原子锁
1 g_nValue++执行
线程A通过寄存器完成加法运算,假设
g_nValue正在加到10000时,线程切换到B,
A的寄存器中保存10000数字,B从10000
开始加数据,当B加到15000时,线程切换
到A,A恢复寄存器的值,A会继续从10000开始
累加,就将B完成5000的加法覆盖.
2 原子锁
执行单个指令时,锁定操作,不允许其他
线程访问.
3 用法
InterlockedIncrement ++运算
InterlockedDecrement --运算
InterlockedCompareExchange ?运算
#include "stdafx.h"
long int g_nValue = 0;
long int g_nValue2 = 0;
DWORD WINAPI Interproc1(LPVOID pParam)
{
for (int n=0; n<1000000; n++)
{
g_nValue++;//普通++ 最后无法得到准确值
}
return 0;
}
DWORD WINAPI Interproc2(LPVOID pParam)
{
for (int n=0; n<1000000; n++)
{
InterlockedIncrement(&g_nValue2);//原子锁++,可以得到准确值
}
return 0;
}
void Create()
{
DWORD nThread = 0;
DWORD nThread2 = 0;
HANDLE hThread[4] = { NULL };
hThread[0] = CreateThread(NULL, 0, Interproc1, NULL, 0, &nThread);
hThread[1] = CreateThread(NULL, 0, Interproc1, NULL, 0, &nThread2);
hThread[2] = CreateThread(NULL, 0, Interproc2, NULL, 0, &nThread);
hThread[3] = CreateThread(NULL, 0, Interproc2, NULL, 0, &nThread2);
WaitForMultipleObjects(4, hThread, TRUE, INFINITE);
printf("g_nValue:%d, g_nValue2:%d\n", g_nValue,g_nValue2);
}
三、临界区
1 临界区作用
线程在执行代码时,将代码锁定,不允
许其他线程执行,只有该线程离开后,
其他线程才能使用这些代码
2 临界区的使用
2.1 初始化临界区
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
//临界区结构地址
);
2.2 临界区加锁
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // pointer to critical
//临界区
);
2.3 临界区解锁
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
// 临界区
);
2.4 释放临界区
VOID DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
//临界区
);
3 和原子锁相比
原子锁是一条语句
临界区可以完成多条语句的锁定.
#include "stdafx.h"
#include "windows.h"
CRITICAL_SECTION g_cs = { 0 };
void Print()
{
printf("LONG -------long ----- \n");
}
DWORD WINAPI CriticalProc(LPVOID pParam)
{
EnterCriticalSection(&g_cs);//临界区加锁
while (1)
{
Print();
Sleep(500);
}
LeaveCriticalSection(&g_cs);//临界区解锁
}
void Create()
{
DWORD nThread = 0;
HANDLE hThread[2] = { 0 };
hThread[0]=CreateThread(NULL, 0 , CriticalProc, 0, 0, &nThread);
hThread[1]=CreateThread(NULL, 0 , CriticalProc, 0, 0, &nThread);
getchar();
}
int main(int argc, char* argv[])
{
InitializeCriticalSection(&g_cs);//初始化临界区
Create();
DeleteCriticalSection(&g_cs);//释放临界区
// printf("Hello World!\n");
return 0;
}
四、 事件
1 事件
通知的作用,当收到事件时,线程可以执行.
否则,线程将等候事件发生.
2 事件的用法
2.1 创建事件
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
//安全属性
BOOL bManualReset,//重置方式
BOOL bInitialState, //初始化状态
LPCTSTR lpName //名称
);
返回创建好的事件句柄
bManualReset - 事件重置方式, TRUE
手动和FALSE自动重置. 如果为FALSE,
系统在等候到事件后,会自动将事件
重置为无信号状态. 如果为TRUE,我们
必须自己使用ResetEvent重置状态.
bInitialState - 初始化状态, TRUE为
有信号,FALSE无信号.
2.2 等候事件
WaitForSingleObject/
WaitForMultipleObjects
2.3 触发事件
BOOL SetEvent(
HANDLE hEvent //事件句柄
);
2.4 关闭事件
CloseHandle
2.5 重置事件
BOOL ResetEvent(
HANDLE hEvent //事件句柄
);
2.6 其他函数
OpenEvent
PulseEvent
#include "stdafx.h"
#include "windows.h"
HANDLE g_hEvent = NULL;
DWORD WINAPI Eventproc1(LPVOID pParam)
{
while(1)
{
SetEvent(g_hEvent);//启动事件
Sleep(4000);
}
}
DWORD WINAPI Eventproc2(LPVOID pParam)
{
while (1)
{ printf("线程2 启动 等待事件激活!\n");
WaitForSingleObject(g_hEvent, INFINITE);//等待事件触发,无限制
printf("Event:%p 触发 \n", g_hEvent);
}
}
void Create()
{
DWORD nThread = 0;
HANDLE hEvent[2] = { NULL };
hEvent[0] = CreateThread(NULL, 0, Eventproc2, NULL, 0, &nThread);
hEvent[1] = CreateThread(NULL, 0, Eventproc1, NULL, 0, &nThread);
//
}
int main(int argc, char* argv[])
{
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL );//注册事件
Create();
getchar();
// printf("Hello World!\n");
CloseHandle(g_hEvent);//关闭事件句柄
return 0;
}
五、 互斥量
1 互斥量
多个线程同时只能有一个执行.
2 互斥量使用
2.1 创建互斥
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
//安全属性
BOOL bInitialOwner,
//初始化的拥有线程
LPCTSTR lpName ); //名称
bInitialOwner - TRUE表示当前创建互斥
量的线程拥有互斥, FALSE为不拥有.
2.2 等候互斥
WaitForSingleObject
WaitForMultipleObjects
2.3 重置互斥
ReleaseMutex
2.4 关闭互斥
CloseHandle
2.5 使用互斥线程,按照谁先等候谁先
拥有互斥量的规则顺序执行.
2.6 其他函数
OpenMutex 打开互斥
#include "stdafx.h"
#include <windows.h>
HANDLE g_hMutex = NULL;
void Print()
{
printf("打印一句话\n");
}
DWORD WINAPI MutexProc1(LPVOID pParam)
{
while (1)
{
//等待互斥
WaitForSingleObject(g_hMutex, INFINITE);
printf("Proc 1 ------ \n");
Sleep(500);
//释放互斥
ReleaseMutex(g_hMutex);
}
return 0;
}
DWORD WINAPI MutexProc2(LPVOID pParam)
{
while (1)
{
//等待互斥
WaitForSingleObject(g_hMutex, INFINITE);
printf("线程 2 #### \n");
Sleep(500);
//释放互斥
ReleaseMutex(g_hMutex);
}
return 0;
}
void Create()
{
DWORD nThread = 0;
HANDLE hThread[2] = { NULL };
hThread[0] = CreateThread(NULL, 0, MutexProc1, NULL, 0, &nThread);
hThread[1] = CreateThread(NULL, 0, MutexProc2, NULL, 0, &nThread);
}
int main(int argc, char* argv[])
{ g_hMutex=
CreateMutex(NULL, FALSE, NULL);
Create();
getchar();
CloseHandle(g_hMutex);
return 0;
}
六、 信号量
1 信号量
通知的作用,和事件类似.但是与事件不同.
事件只维护一个值0或者1.
信号量维护一个变量,0时无信号,大于0有
信号.
2 信号量的使用
2.1 创建信号量
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
//安全属性
LONG lInitialCount,//初始信号量
LONG lMaximumCount,//最大信号量
LPCTSTR lpName //命名
);
返回创建好的信号量句柄.
2.2 等候信号量
WaitForSingleObject
WaitForMultipleObjects
2.3 释放信号
BOOL ReleaseSemaphore(
HANDLE hSemaphore, //信号量句柄
LONG lReleaseCount,//释放信号的数量
LPLONG lpPreviousCount //释放前的数量
);
2.4 关闭信号量
CloseHandle
2.5 打开信号量
OpenSemaphore
#include "stdafx.h"
#include "windows.h"
HANDLE g_hSemaphore = NULL;
void Print()
{
}
DWORD WINAPI ThreadSend(LPVOID pParam)
{
while (1)
{
ReleaseSemaphore(g_hSemaphore,1, NULL);//每秒释放一个信号
Sleep(1000);
}
return 0;
}
DWORD WINAPI ThreadRecv(LPVOID pParam)
{
while (1)
{ //等候信号量的信号
WaitForSingleObject(g_hSemaphore, INFINITE);
printf("Hello Semaphore \n");
Sleep(100);
}
return 0;
}
void Create()
{
DWORD nThread = 0;
HANDLE hThread[2] = { NULL };
hThread[0] = CreateThread(NULL, 0 , ThreadSend, NULL, 0, &nThread);
hThread[1] = CreateThread(NULL, 0 , ThreadRecv, NULL, 0, &nThread);
getchar();
}
int main(int argc, char* argv[])
{
g_hSemaphore = CreateSemaphore(NULL, 5, 10, NULL);
Create();
CloseHandle(g_hSemaphore);
return 0;
}
七、 可等候定时器
1 可等候定时器
是一个更加精确系统提供的定时器.能够
达到100ns级别.
2 定时器的使用
2.1 创建定时器
HANDLE CreateWaitableTimer(
LPSECURITY_ATTRIBUTES lpTimerAttributes,
//安全属性
BOOL bManualReset,//重置方式
LPCTSTR lpTimerName //命名
);
返回创建好的定时器的句柄
2.2 设置定时器
BOOL SetWaitableTimer(
HANDLE hTimer, //定时器句柄
const LARGE_INTEGER *pDueTime,
//定时器第一次触发的时间,100ns级别
LONG lPeriod,
//后续每次触发的间隔,毫秒级别
PTIMERAPCROUTINE pfnCompletionRoutine,
//APC处理函数
LPVOID lpArgToCompletionRoutine,
//APC参数
BOOL fResume ); //休眠标识
pDueTime - 正值,表示绝对时间
负值,表示相对于现在的时间间隔
lPeriod - 0 定时器不再有后续触发
大于0 按照间隔触发
pDueTime | lPeriod | lPeriod ....
2.3 等候定时器
WaitForSingleObject
WaitForMultipleObjects
2.4 关闭定时器
CloseHandle
2.5 APC定时器
VOID CALLBACK TimerAPCProc(
LPVOID lpArgToCompletionRoutine // data value
DWORD dwTimerLowValue // timer low value
DWORD dwTimerHighValue // timer high value
);
2.6 其他
OpenWaitableTimer 打开
CancelWaitableTimer 取消