所有同步方法
1. 线程同步
问题:多个线程对同一个回调函数操作,导致一段代码未执行完就开始切换下一个线程,造成数据混乱。为了不造成混乱,使用了限定操作 同步
1.1 原子操作 -- 单值
Interlocked function 函数里有关原子操作
互锁函数同步对多个线程共享的变量的访问。它们的目的是防止线程在递增或检查变量时被抢占。不同进程的线程可以使用这些函数,因为它们的变量共享内存。Windows CE支持三个互锁函数
InterlockedIncrement 增加一个共享变量并检查结果值
InterlockedDecrement 递减共享变量并检查结果值
InterlockedExchange 交换指定变量的值
InterlockedTestExchange 只有当其中一个变量是特定的,才交换指定变量的值
InterlockedExchangeAdd -- 加法操作不可打断
InterlockExchangeAdd函数向addend变量执行增量值的原子加法。该函数防止多个线程同时使用同一个变量。
1.2 关键段
使用关键段首先先要定义一个结构体变量
CRITICAL_SECTION cs;
这是一个传出变量
//初始化关键段
InitializeCriticalSection(&cs);
//如果锁着,则等待,如果没有锁着,则返回, 并上锁
EnterCriticalSection(&cs)
在同一个线程调用,会增加引用计数,几次enter,需要对应次数的leave
//解锁,让出所有权
LeaveCriticalSection(&cs);
EnterCriticalSection(&cs)和LeaveCriticalSection(&cs)必须成对使用;
TryEnterCriticalSection(&cs); 有返回值
TRUE,进入关键段,并上锁
FALSE,其他线程在使用
LeaveCriticalSection(&cs)
TryEnterCriticalSection和EnterCriticalSection使用方式相同 不过有一个BOOL的返回值表示是否获得所有权
InitializeCriticalSection(&cs); 初始化
DeleteCriticalSection(&cs); 反初始化
1.3 内核同步对象(事件, 信号灯, 互斥体)
在多线程的情况下,有时候我们会希望等待某一线程完成了再继续做其他事情,要实现这个目的,可以使用Windows API函数WaitForSingleObject,或者WaitForMultipleObjects。这两个函数都会等待Object被标为有信号(signaled)时才返回的。
那么,信号是什么呢?首先我们可以假设这里存在一个文件和两个线程,我们规定这个文件同一时刻只能被一个线程所访问打开,那么我们的线程该如何知道这个文件现在有没有被别的线程访问呢?我们可以让线程等在一个死循环里,这个循环之一在尝试打开访问这个文件,直到能够打开为止;这样做虽然可以实现目的,但是死循环会占用大量的内存,所以windows就设置了信号量。信号量的作用简单理解就是一个标志位,在我们上述的问题中,这个文件就有一个信号量,初始时我们设信号量为FALSE,而只有当信号量为FALSE时线程才可以打开访问这个文件。那么,当第一个线程到达,信号量为FALSE,线程打开文件进行访问,并将信号量置为TRUE;在第一个线程在访问文件时,第二个线程到来,此时信号量仍未TRUE,所以第二个线程等待,这个等待的过程就是WaitForSingleObject。WaitForSingleObject在等待的过程中会进入一个非常高效的沉睡等待状态,只占用极少的CPU时间片。
state(状态)
signaled -- 已触发, 开锁
no-signal -- 未触发, 关锁
process/线程
signaled -- 结束运行
no-signal -- 正在运行
检测方式:
WaitForSingleObject等待的对象Event,Mutex,Semaphore,Process,Thread
有三种返回类型:
WAIT_OBJECT_0, 表示等待的对象有信号(对线程来说,表示执行结束);
WAIT_TIMEOUT, 表示等待指定时间内,对象一直没有信号(线程没执行完);
WAIT_ABANDONED 表示对象有信号,但还是不能执行 一般是因为未获取到锁或其他原因
WaitForSingleObject -- 等待一个对象
DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object 对象句柄 可以是信号或者进程
DWORD dwMilliseconds // time-out interval);等待时间
INFINITE 一直等时间结束 宏的值为-1
WaitForMultipleObjects -- 等待多个对象
DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in array 个数
CONST HANDLE *lpHandles, // object-handle array 句柄数组
BOOL bWaitAll, // wait option 等待选项
DWORD dwMilliseconds // time-out interval);等待时间
1.4 事件(event)
事件对象就像一个开关:它只有两种状态---开和关。当一个事件处于”开”状态,我们称其为”有信号”否则称为”无信号”。可以在一个线程的执行函数中创建一个事件对象,然后观察它的状态,如果是”无信号”就让该线程睡眠,这样该线程占用的CPU时间就比较少。
一个Event被创建以后,可以
用OpenEvent()API来获得它的Handle
用CloseHandle() 来关闭它,
用SetEvent()或PulseEvent()来设置它使其有信号,
用ResetEvent()来使其无信号,
用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号.
CreateEvent -- 创建事件对象
HANDLECreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes, // 安全属性
BOOLbManualReset, // 复位方式 true表示 手工设置触发和未触发 false表示默认系统设置 一般系统直接设置为未触发状态
BOOLbInitialState, // 初始状态 true表示开始触发
LPCTSTRlpName // 对象名称 若是本进程使用不需要跨进程 跨进程配合OpenEvent使用
);
SetEvent -- 将事件对象设置为已触发(开锁)状态
ResetEvent -- 将事件对象设置为未触发(关锁)状态
OpenEvent -- 通过名字打开一个事件对象
HANDLEOpenEvent(
DWORD dwDesiredAccess,EVENT_ALL_ACCESS 指定事件对象所有可能的权限
BOOL bInheritHandle,句柄是否继承
LPCTSTR lpName即将要打开的事件对象的名字
);