15、Windows API 线程同步
一、基本概念
常见的线程同步方法(对象)有:
互斥对象(Mutex),事件对象(Event),信号量(Semaphore),临界区(critical section)等。
Synchronization Functions[1]
在同步过程中,两个最重要的概念是同步对象和等待函数。
在线程同步过程中,需要先定义一个同步对象,同步对象一般具有两种状态:标志的(置位,signaled)和未标志的(未置位,nonsignaled)。线程根据是否已经完成操作将同步对象设置为标志的或未标志的。
而等待函数的功能是专门用于等待同步对象状态改变。一个线程调用等待函数后执行会暂停,直到同步对象的状态改变后,等待函数才会返回,线程才会继续执行。等待函数分为“单对象”等待函数和“多对象”等待函数。
What is Signaled State?
One of two possible states for kernel-defined dispatcher objects, which support
synchronization. When the kernel sets such an object to the Signaled state, any threads
waiting on the object are released from their wait and become eligible for execution.
Wait handles have two states, signaled and nonsignaled. A wait handle that is not
owned by any thread is in the signaled state. A wait handle that is owned by a thread is in
the nonsignaled state.[2]
二、同步对象示例[3]
使用事件对象示例
演示使用Event同步线程
**************************************/
/* 头文件 */
#include <windows.h>
#include <stdio.h>
/* 常量定义 */
#define NUMTHREADS 3
#define BUFFER_SIZE 16
#define FOR_TIMES 5
/* 全局变量 */
HANDLE hEvent; // 用于同步
BYTE lpSharedBuffer[16] = {0}; // 共享内存
/* 函数声明 */
void UseEvents(void);
DWORD WINAPI EventFunction(LPVOID lpParam);
/*************************************
* int main(void)
* 功能 演示
*
* 参数 未使用
**************************************/
int main()
{
UseEvents();
}
/*************************************
* void UseEvents(void)
* 功能 演示Event的使用方法
*
* 参数 未使用
**************************************/
void UseEvents(void)
{
HANDLE hThread;
hEvent = CreateEvent(
NULL, // 默认安全属性
TRUE, // 手工重置
FALSE, // 初始为未置位的
NULL // 未命名
);
// 判断是否创建成功
if (hEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// 创建线程
hThread = CreateThread(NULL, 0,
EventFunction,
NULL,
0, NULL);
if (hThread == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
Sleep(2000); // 可以做一些其他处理
// 向共享内存中复制数据
CopyMemory(lpSharedBuffer,"Event",lstrlen("Event"));
// 设置 Event 使ThreadFunction线程可以开始复制数据
SetEvent(hEvent);
}
/*************************************
* DWORD WINAPI EventFunction(LPVOID lpParam)
* 功能 线程函数,读共享内存
*
* 参数 未使用
**************************************/
DWORD WINAPI EventFunction(LPVOID lpParam)
{
DWORD dwWaitResult;
// 等待,直到事件被置位
dwWaitResult = WaitForSingleObject(
hEvent, // Event 句柄
INFINITE); // 无限等待
if (dwWaitResult != WAIT_OBJECT_0)
{
printf("Wait error: %d\n", GetLastError());
return 0;
}
// 读共享内存
printf(lpSharedBuffer);
// 重置事件
if (! ResetEvent(hEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return 0;
}
return 1;
}
演示使用Event同步线程
**************************************/
/* 头文件 */
#include <windows.h>
#include <stdio.h>
/* 常量定义 */
#define NUMTHREADS 3
#define BUFFER_SIZE 16
#define FOR_TIMES 5
/* 全局变量 */
HANDLE hWriteEvent[NUMTHREADS]; // 写Event 表示写操作是否完成
HANDLE hReadEvents[NUMTHREADS]; // 读Event 表示读操作是否完成
BYTE lpSharedBuffer[16] = {0}; // 共享内存
/* 函数声明 */
void MultiEvents(void);
VOID WriteToBuffer(VOID);
DWORD WINAPI ThreadFunction(LPVOID lpParam);
/*************************************
* int main(void)
* 功能 演示
*
* 参数 未使用
**************************************/
int main()
{
MultiEvents();
}
/*************************************
* void UseEvents(void)
* 功能 演示Event的使用方法
*
* 参数 未使用
**************************************/
void MultiEvents(void)
{
HANDLE hThread;
DWORD i;
// 创建多个线程,读共享内存,主线程写共享内存。
// 每个线程都有对应的读写同步事件
for(i = 0; i < NUMTHREADS; i++)
{
// 每个线程都有一个Event表示写入操作完成
hWriteEvent[i] = CreateEvent(
NULL, // 默认安全属性
FALSE, // 自动重置
FALSE, // 初始为未置位的
NULL // 未命名
);
// 判断是否创建成功
if (hWriteEvent[i] == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// 每个读线程有一个Event表示读操作已经完成
hReadEvents[i] = CreateEvent(
NULL, // 默认安全属性
FALSE, // 自动重置
FALSE, // 初始化为未置位的
NULL); // 未命名
if (hReadEvents[i] == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// 创建线程
hThread = CreateThread(NULL, 0,
ThreadFunction,
(LPVOID)i, // Event对象句柄作为
0, NULL);
if (hThread == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
WriteToBuffer();
}
/*************************************
* VOID WriteToBuffer(INT iContent)
* 功能 由主线程调用,向共享内存中写入数据
* 等待所有读线程读完后函数返回
*
* 参数 未使用
**************************************/
VOID WriteToBuffer(VOID)
{
DWORD dwWaitResult, j,i;
// 完成 FOR_TIMES 次读写
for (j = 0; j < FOR_TIMES; j++)
{
Sleep(rand()%100); // 写入需要的时间随机
// 写入共享内存
wsprintf(lpSharedBuffer,"shared %d",j);
// 将线程对应的写Event置为“标志的”,表示写操作完成,
// 其他线程可以开始读
for(i=0; i<NUMTHREADS; i++)
{
if (! SetEvent(hWriteEvent[i]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return;
}
}
// 等待所有的线程读完,开始下次写入
dwWaitResult = WaitForMultipleObjects(
NUMTHREADS, // Event句柄的个数
hReadEvents, // Event句柄数组
TRUE, // 等到所有的Event都被标志
INFINITE); // 无限等待
// 判断等待结果
if (dwWaitResult != WAIT_OBJECT_0)
{
printf("Wait error: %d\n", GetLastError());
ExitProcess(0);
}
}
}
/*************************************
* DWORD WINAPI ThreadFunction(LPVOID lpParam)
* 功能 线程函数,读共享内存
*
* 参数 LPVOID lpParamt 实际为指向Event句柄的指针
**************************************/
DWORD WINAPI ThreadFunction(LPVOID lpParam)
{
DWORD dwWaitResult;
BYTE lpRead[16];
DWORD j = 0;
DWORD dwThreadIndex = (DWORD)lpParam;
// 完成 FOR_TIMES 次读写
for(; j<FOR_TIMES; j++)
{
// 等待写事件置位,表示数据已经写入
dwWaitResult = WaitForSingleObject(
hWriteEvent[dwThreadIndex], // Event 句柄
INFINITE); // 无限等待
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
Sleep(rand()%10); // 模拟数据处理所需的时间随机
CopyMemory(lpRead,lpSharedBuffer,16);
break;
// 发生错误
default:
printf("Wait error: %d\n", GetLastError());
ExitThread(0);
}
// 将读Event置位,表示读操作完成
if (! SetEvent(hReadEvents[dwThreadIndex]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return 0;
}
//打印读到的内容
printf("线程 %u\t第 %d 次读,内容:%s\n",
dwThreadIndex,j,(LPSTR)lpRead);
}
return 1;
}
[1] http://msdn.microsoft.com/en-us/library/ms686360%28VS.85%29.aspx
[2] http://msdn.microsoft.com/en-us/library/fxy8dte8%28VS.71%29.aspx
[3] 精通Windows API 函数、接口、编程实例
[4] http://www.cnblogs.com/mydomain/archive/2010/11/07/1870994.html