线程:需要同步对象在某些时候进行同步操作。
基本原理
线程同步机制是为各线程能够协同工作而设计的。同步是唯一保证共享数据持久的方法。
在同步过程中,两最重要的概念是同步对象(Mutex,Semaphore,Event,CriticalSection)和等待函数((WaitForSingleObject(),WaitForMultipleObjects())。同步对象是内存中的变量,你可以像访问一般的数据那样来访问他。
在线程同步过程中,需要先定义一个同步对象,同步对象一般具有两种状态:标志的(置位,signaled)和未标志的(未置位,nonsignaled)。线程根据是否已经完成操作将同步对象设置为标志的或未标志的。
而等待函数的功能是专门用于等待同步对象状态改变。一个线程调用等待函数后执行会暂停,直到同步对象的状态改变后,等待函数才会返回,线程才会继续执行。等待函数分为i“单对象”等待函数和“多对象”等待函数。
小demo
/*
实例中UseEvents先创建一个事件对象,然后创建了线程,创建完成线程后,经过一段时间,向内存中复制数据,然后再置位。
被创建的线程EventFunction调用WaitForSingleObject函数等待事件置位,事件置位后,再读取内存,再复位事件(使未置位).
*/
#include <windows.h>
#include <stdio.h>
/* 全局变量 */
HANDLE hEvent; // 用于同步
BYTE lpSharedBuffer[16] = {0}; // 共享内存
/* 函数声明 */
void UseEvents(void);
DWORD WINAPI EventFunction(LPVOID lpParam);
int main()
{
UseEvents();
system("pause");
return 0;
}
void UseEvents(void)
{
hEvent = CreateEvent(
NULL, // 默认安全属性
TRUE, // 手工重置
FALSE, // 初始为未置位的
NULL // 未命名
);
if (hEvent == NULL) // 判断是否创建成功
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
HANDLE hThread;
for(int a=0;a<10;a++)// 创建线程
{
hThread = CreateThread(NULL, 0, EventFunction, NULL,0, NULL);
if (hThread == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
Sleep(100); // 可以做一些其他处理
}
CopyMemory(lpSharedBuffer,"Event",lstrlen("Event"));// 向共享内存中复制数据
SetEvent(hEvent);// 设置 Event 使ThreadFunction线程可以开始复制数据
}
//线程函数,读共享内存
DWORD WINAPI EventFunction(LPVOID lpParam)
{
// 等待,直到事件被置位
DWORD dwWaitResult = WaitForSingleObject(
hEvent, // Event 句柄
INFINITE); // 无限等待
if (dwWaitResult != WAIT_OBJECT_0)
{
printf("Wait error: %d\n", GetLastError());
return 0;
}
printf("%s\n",lpSharedBuffer);// 读共享内存
if (! ResetEvent(hEvent) ) // 重置事件
{
printf("SetEvent failed (%d)\n", GetLastError());
return 0;
}
return 1;
}
线程同步的过程
同步对象与等待函数相互配合以实现线程同步。比如,线程A在进行某种操作前需要线程B为其准备好数据,那忙这种时候就需要线程同步。线程A会等待线程B的执行,直到需要的数据准备好。那么使用同步对象和等待函数,其过程基本如下:
- 在需要进行线程同步的进程中定义某种同步对象,同步对象必需是全局的,以保证需要同步的线程A和B都可以访问到同步对象。
- 开始时,线程A和B相互独立地执行。
- 线程B在准备好线程A需要使用到的数据前,将同步对象置位"未标记的",线程B在准备好线程A需要使用的数据后,改变同步对象的状态,置为"标记的"。
- 线程A运行,直到需要线程B为其准备的数据时,调用等待函数。如果同步对象不是"标记的",则一直等待到同步对象的状态改变到"标记的"为止。同步对象被B设置为"标记的"后(表示线程B已经完成数据准备工作),等待函数才会返回,线程A继续执行。
- 以此类推,循环往复。