线程
线程是计算机中最小的执行单元。
一个进程中的所有线程共享该进程的地址空间,所以,在同一个进程中可以实现多个线程间的相互通信。
#include<windows.h>
创建线程:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 指定线程的安全属性
SIZE_T dwStackSize, // initial stack size,指定为0,则新创建线程地址空间大小和调用该函数的线程地址空间大小一样
LPTHREAD_START_ROUTINE lpStartAddress, // thread function,指定该线程线程函数的地址。
LPVOID lpParameter, // thread argument 将要传递给新建线程的命令行参数
DWORD dwCreationFlags, // creation option 创建后是否立即执行。CREATE_SUSPENDED(暂停运行);0(立即运行)
LPDWORD lpThreadId // thread identifier 新建线程ID号,可以设置为NULL。
);
例如:
HANDLE h;
h=::CreateThread(NULL,0,Proc1,NULL,0,NULL);
线程函数(需要提前声明):
DWORD WINAPI Proc1( LPVOID LpParameter)
{
...............
return 0;
}
关闭线程句柄对象
::CloseHandle(h) //并没有终止新建的线程,只是表示在主线程中对新创建的线程的引用不感兴趣。
线程同步
同一个进程中的多个线程相互协调工作达到一致性
用户编写的程序,有时多个代码段同时读取或者修改相同地址空间中的共享数据,这个时候就需要线程同步技术来协调
方法一:临界区对象:(关键代码段)
临界区对象:用代码段使得资源独享
两种方法:使用API函数和MFC类对临界区对象进行编程
API函数操作临界区
创建临界区:
CRITICAL_SECTION Section; //定义临界区对象
临界区初始化:调用InitializeCriticalSection()对临界区对象初始化。
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
InitializeCriticalSection(&Section); //初始化临界区对象
进入该临界区:调用EnterCriticalSection()函数
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
EnterCriticalSection(&Section) //进入临界区
释放并离开临界区:调用LeaveCriticalSection()函数
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
LeaveCriticalSection(&Section); //离开临界区
MFC使用CCriticalSection类操作临界区
创建临界区对象:
CCriticalSection Section;
锁定临界区:调用类成员函数Lock()
if(Section.Lock()){.......} //调用锁定函数临界区,成功返回True,失败返回false
释放临界区:调用类成员函数UnLock()
Section.Unlock();
方法二:事件对象
事件对象是指用户在程序中使用内核对象的有无信号状态实现线程的同步。
事件对象属于内核对象,包含以下三个成员:
1、使用计数
2、指明该事件是一个“自动重置的事件”还是“人工重置的事件”的布尔值。
3、指明该事件是一个“已通知状态”和“未通知状态”的布尔值
“人工重置事件“对象得到”信号“——>该事件的所有线程变为可调度线程
“自动重置事件”对象得到“信号”——>该事件的线程中只有一个线程变成“可调度”
API操作事件对象
创建并返回事件对象: CreateEvent()
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // SD , NULL
BOOL bManualReset, // reset type true表示为“人工重置对象”,false表示为“自动重置对象”
BOOL bInitialState, // initial state true表示事件对象初始时为'有信号"状态,false反之
LPCTSTR lpName // object name 事件对象名称。若我NULL,则创建的是一个匿名对象
);
注:若bManualReset设置为true,则事件收到信号时,所有线程都可以调度,则失去了同步的意义。所以为了能够实现线程同步,一般使用自动重置false
HANDLE hevent;
hevent=::CreateEvent(NULL,false,true,NULL); //创建一个有信号自动重置匿名事件。
手动设置为有信号状态:BOOL SetEvent(HANDLE hEvent);
如果创建事件对象时,将其设置为无信号状态,则用户需要手动将其设置为有信号状态。
将事件设置为无信号状态:BOOL ResetEvent();
主动请求事件对象: WaitForSingleObject()
DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object 事件对象句柄
DWORD dwMilliseconds // time-out interval 等待事件,若为INFINITE,表示永远等待
);
返回值----------表用返回原因:
WAIT_TIMEOUT 用户等待的时间已过
WAIT_OBJECT_0 线程所请求的事件已经是有信号状态
::WaitForSingleObject(hevent,INFINITE) //请求事件对象
MFC使用CEvent类实现线程同步
CEvent类是 MFC中支持事件编程的类
调用该类的构造函数创建对象。构造函数原型:
CEvent( BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );BOOL bInitiallyOwn 为false表示无信号状态,true表示为有信号状态。
CEvent event(true,false,NULL,NULL); //创建事件类对象
请求事件对象:依然使用的API WaitForSingleObject();
::WaitForSingleObject(event.m_hObject,INFINITE); //注意,用的是event.m_hObject
将事件对象设置为有信号或者是无信号:
BOOL SetEvent();
BOOL ReSetEvent();
方法三:互斥对象
互斥对象用于实现线程同步,但是互斥对象还可以在进程之间使用
在互斥对象中包含一个线程ID和一个计数器。
使用API函数操作互斥对象
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // SD
BOOL bInitialOwner, // initial owner 表示该互斥对象的所有者。如果为true表示,创建该互斥对象的线程拥有其所有权。false,表示创建线程不能拥有所有权
LPCTSTR lpName // object name 互斥对象的名称。若为NULL,则表示程序创建的是匿名对象
);
HANDLE hmutex; //声明互斥对象句柄
hmutex=::CreateMutex(NULL,FALSE,NULL); //创建互斥对象并返回其句柄
释放对互斥对象的所有权:ReleaseMutex()
线程使用完该互斥对象以后,应该调用函数ReleaseMutex释放对该互斥对象的所有权,也就是让互斥对象处于有信号状态。
::ReleaseMutex(hmutex); //释放互斥对象
在互斥对象中,线程也可以调用函数WaitForSingleObject()对该对象进行请求。
注:在使用互斥对象编程时,那个线程拥有其所有权,哪个线程就应该释放互斥对象。
MFC使用CMutex类为互斥对象类
创建CMutex类对象是通过其构造函数实现的。
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTRlpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL ); // 参数意义和上面相同
CMutex mex(FLASE,NULL,NULL); //创建互斥对象
对互斥对象所保护的区域进行锁定和解锁
virtual BOOL Lock (DWORD dwTimeout = INFINITE);
virtual BOOL Unlock(LONG lCount,LPLONG lpPrevCount=NULL); //其参数lCount是默认参数,用户在使用时可以不为其指定值。
mex.Lock(INFINITE); //锁定互斥对象
mex.Unlock(0,0) //释放互斥对象