http://blog.sina.com.cn/s/blog_499b249c010003nw.html
Win32API多线程程序设计学习笔记
CreateThread
函数功能:该函数创建一个在调用进程的地址空间中执行的线程。
函数原型:
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//描述安全性,用NULL表示使用缺省值,推荐
DWORD dwStackSize,
//新线程拥有自己的堆栈,0表示使用缺省值1MB,推荐
LPTHREAD_START_ROUTINE
lpStartAddress,
//新线程的起始地址,放线程函数名称
LPVOID
lpParameter,
//此值被传送到线程函数去作为参数
DWORD dwCreationFlags,
//允许产生一个暂时挂起的线程,默认是立即开始执行
LPDWORD lpThreadld
);
//新线程的ID被传到这
CreateThread
函数功能:该函数创建一个在调用进程的地址空间中执行的线程。
函数原型:
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//描述安全性,用NULL表示使用缺省值,推荐
DWORD dwStackSize,
//新线程拥有自己的堆栈,0表示使用缺省值1MB,推荐
LPTHREAD_START_ROUTINE
//新线程的起始地址,放线程函数名称
LPVOID
//此值被传送到线程函数去作为参数
DWORD dwCreationFlags,
//允许产生一个暂时挂起的线程,默认是立即开始执行
LPDWORD lpThreadld
//新线程的ID被传到这
CloseHandle
函数功能:完成工作后,调用CloseHandle释放核心对象
函数原型:
BOOL CloseHandle(
//代表一个已打开的对象HANDLE
HANDLE hObject;
);
PS:
closehandle()在线程开始以后就调用了,这时候,它就把线程的句柄给关闭了,而线程还在继续运行,直到线程运行完毕。closehandel就是把线程的引用计数减一,当减到0的时候,线程的句柄就被系统销毁了,但线程还在,直到结束
函数功能:完成工作后,调用CloseHandle释放核心对象
函数原型:
BOOL CloseHandle(
);
PS:
WaitForMultipleObjects
函数功能:
WaiForMultipleObjects函数等待几个线程的激发,当所等待的线程都激发或者时间片用尽而返回。由参数不同而不同。
函数原型:
DWORD WaitForMultipleObjects(
//表示等待几个线程
DWORD nCount,
//输入要等待的线程的名称,前提是一个线程的数组
CONST HANDLE *lpHandles,
//如果这个里面填入true,那么必须所有等待的线程都激发才能返回,如果填false,那么只要有一个handle激发了次函数就能返回
BOOL fWaitAll,
//如果是0,那么到这里就立刻结束了,如果是INFINITE,就无穷等待,直到,要等待的线程都激发为止
DWORD dwMilliSeconds
);
函数功能:
函数原型:
DWORD WaitForMultipleObjects(
DWORD nCount,
//输入要等待的线程的名称,前提是一个线程的数组
CONST HANDLE *lpHandles,
//如果这个里面填入true,那么必须所有等待的线程都激发才能返回,如果填false,那么只要有一个handle激发了次函数就能返回
BOOL fWaitAll,
//如果是0,那么到这里就立刻结束了,如果是INFINITE,就无穷等待,直到,要等待的线程都激发为止
DWORD dwMilliSeconds
Sleep
函数功能:该函数对于指定的时间间隔挂起当前的执行线程。
函数原型:
VOID Sleep(DWORD dwMilliseconds);
//里面填入要挂起的时间间隔,以毫秒为单位
函数功能:该函数对于指定的时间间隔挂起当前的执行线程。
函数原型:
VOID Sleep(DWORD dwMilliseconds);
//里面填入要挂起的时间间隔,以毫秒为单位
WaitForSingleObject
函数功能:当如下情况之一发生时该函数返回:指定对象处于信号态;超时。
函数原型:
DWORD
WaitForSingleObject(
//等待对象的线程HANDLE,或者仅仅是一个句柄,如信号量
HANDLE hHandle,
//如果输入一个以毫秒为单位的时间,则等待的最长时间就是那个
当时间片结束时不管是否HANDLE已激活,都返回
如果输入0,则立刻返回
如果输入INFINITE,则无穷等待,直到HANDLE激活为止
DWORD dwMilliseconds
);
函数功能:当如下情况之一发生时该函数返回:指定对象处于信号态;超时。
函数原型:
DWORD
HANDLE hHandle,
//如果输入一个以毫秒为单位的时间,则等待的最长时间就是那个
当时间片结束时不管是否HANDLE已激活,都返回
DWORD dwMilliseconds
);
CreatSemaphore
函数功能:该函数是创建一个有名或者无名信号对象。(锁定一次现值减一)
函数原型:
HANDLE CreateSemaphore(
//安全属性,NULL表示默认属性,推荐
LPSECURITY_ATTRIBUTES lpAttributes,
//信号量的初值,必须大于等于0,小于等于所定义的最大数量
LONG lInitialCount,
//信号量的最大值,是自己定义的,
LONG lMaximumCount,
//信号量的名称,如“FULL”,如果是NULL,则此信号量无名称
LPCTSTR lpName
);
函数功能:该函数是创建一个有名或者无名信号对象。(锁定一次现值减一)
函数原型:
HANDLE CreateSemaphore(
//安全属性,NULL表示默认属性,推荐
LPSECURITY_ATTRIBUTES lpAttributes,
//信号量的初值,必须大于等于0,小于等于所定义的最大数量
LONG lInitialCount,
//信号量的最大值,是自己定义的,
LONG lMaximumCount,
//信号量的名称,如“FULL”,如果是NULL,则此信号量无名称
LPCTSTR lpName
ReleaseSemaphore
函数功能:该函数将指定信号对象的计数增加一个指定的数量。
函数原型:
ReleaseSemaphore(
);
一个信号量,只要锁定一次就会自减,只要释放一次,就会自加
CreateMutex
函数功能:该函数是创建有名或者无名的互斥对象。
函数原型:
HANDLE CreateMutex(
//安全属性,NULL表示使用默认值
LPSECURITY_ATTRIBUTES lpMutexAttributes,
//填入TRUE表示此互斥量可以被调用
BOOL bInitialOwner,
//互斥量的名称,在这里可以自己指定
LPCTSTR lpName
);
函数功能:该函数是创建有名或者无名的互斥对象。
函数原型:
HANDLE CreateMutex(
//安全属性,NULL表示使用默认值
LPSECURITY_ATTRIBUTES lpMutexAttributes,
//填入TRUE表示此互斥量可以被调用
BOOL bInitialOwner,
//互斥量的名称,在这里可以自己指定
LPCTSTR lpName
);
ReleaseMutex
函数功能:该函数放弃指定互斥对象的拥有权。每使用一次,MUTEX引用计数自加1;每释放一次,MUTEX引用计数自减1,当引用计数减到0时,此互斥量销毁,这一点如果使用CloseHandle(),也可以达到相同的效果,如果只引用了一次MUTEX而同时使用了ReleaseMutex(),和CloseHandle(),那么这个MUTEX直接销毁了。
函数原型:
BOOL ReleaseMutex(
//想要释放的信号量句柄
HANDLE hMutex
);
函数功能:该函数放弃指定互斥对象的拥有权。每使用一次,MUTEX引用计数自加1;每释放一次,MUTEX引用计数自减1,当引用计数减到0时,此互斥量销毁,这一点如果使用CloseHandle(),也可以达到相同的效果,如果只引用了一次MUTEX而同时使用了ReleaseMutex(),和CloseHandle(),那么这个MUTEX直接销毁了。
函数原型:
BOOL ReleaseMutex(
HANDLE hMutex
);
GetExitCodeThread
函数功能:提供一个返回的线程结束代码,用以判断是否线程结束了。
通过调用此函数,同时使用在createthread中获得的线程ID作为参数,就可以得到线程的结束代码。传回的值为TRUE或FALSE。
如果线程已结束,那么线程的结束代码会被放在lpExitCode参数中带回来,如果线程尚未结束,那么带回来的值是STILL_ACTIVE
函数原型:
BOOL GetExitCodeThread(
//这里放由creatThread传回的线程ID
HANDLE hThread;
//指向一个DWORD用以接受结束代码(EXITCODE)
LPWORD lpExitCode;
);
函数功能:提供一个返回的线程结束代码,用以判断是否线程结束了。
通过调用此函数,同时使用在createthread中获得的线程ID作为参数,就可以得到线程的结束代码。传回的值为TRUE或FALSE。
函数原型:
BOOL GetExitCodeThread(
事件(Event)是WIN32提供的最灵活的线程间同步方式,事件可以处于激发状态(signaled or true)或未激发状态(unsignal or false)。根据状态变迁方式的不同,事件可分为两类:
(1)手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。
(2)自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。
创建事件的函数原型为:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // SECURITY_ATTRIBUTES结构指针,可为NULL BOOL bManualReset, // 手动/自动 // TRUE:在WaitForSingleObject后必须手动调用ResetEvent清除信号 // FALSE:在WaitForSingleObject后,系统自动清除事件信号 BOOL bInitialState, //初始状态 LPCTSTR lpName //事件的名称 ); |
使用"事件"机制应注意以下事项:
(1)如果跨进程访问事件,必须对事件命名,在对事件命名的时候,要注意不要与系统命名空间中的其它全局命名对象冲突;
(2)事件是否要自动恢复;
(3)事件的初始状态设置。
由于event对象属于内核对象,故进程B可以调用OpenEvent函数通过对象的名字获得进程A中event对象的句柄,然后将这个句柄用于ResetEvent、SetEvent和WaitForMultipleObjects等函数中。此法可以实现一个进程的线程控制另一进程中线程的运行,例如:
HANDLE hEvent=OpenEvent(EVENT_ALL_ACCESS,true,"MyEvent"); ResetEvent(hEvent); |
临界区
定义临界区变量
通常情况下,CRITICAL_SECTION结构体应该被定义为全局变量,以便于进程中的所有线程方便地按照变量名来引用该结构体。
初始化临界区
该函数用于对pcs所指的CRITICAL_SECTION结构体进行初始化。该函数只是设置了一些成员变量,它的运行一般不会失败,因此它采用了VOID类型的返回值。该函数必须在任何线程调用EnterCriticalSection函数之前被调用,如果一个线程试图进入一个未初始化的CRTICAL_SECTION,那么结果将是很难预计的。
删除临界区
进入临界区
离开临界区
使用临界区编程的一般方法是:
关于临界区的使用,有下列注意点:
(1)每个共享资源使用一个CRITICAL_SECTION变量;
(2)不要长时间运行关键代码段,当一个关键代码段长时间运行时,其他线程就会进入等待状态,这会降低应用程序的运行性能;
(3)如果需要同时访问多个资源,则可能连续调用EnterCriticalSection;
(4)Critical Section不是OS核心对象,如果进入临界区的线程"挂"了,将无法释放临界资源。这个缺点在Mutex中得到了弥补。
定义临界区变量
CRITICAL_SECTION gCriticalSection; |
通常情况下,CRITICAL_SECTION结构体应该被定义为全局变量,以便于进程中的所有线程方便地按照变量名来引用该结构体。
初始化临界区
VOID WINAPI InitializeCriticalSectio LPCRITICAL_SECTION lpCriticalSection //指向程序员定义的CRITICAL_SECTION变量 ); |
该函数用于对pcs所指的CRITICAL_SECTION结构体进行初始化。该函数只是设置了一些成员变量,它的运行一般不会失败,因此它采用了VOID类型的返回值。该函数必须在任何线程调用EnterCriticalSection函数之前被调用,如果一个线程试图进入一个未初始化的CRTICAL_SECTION,那么结果将是很难预计的。
删除临界区
VOID WINAPI DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection //指向一个不再需要的CRITICAL_SECTION变量 ); |
进入临界区
VOID WINAPI EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection //指向一个你即将锁定的CRITICAL_SECTION变量 ); |
离开临界区
VOID WINAPI LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection //指向一个你即将离开的CRITICAL_SECTION变量 ); |
使用临界区编程的一般方法是:
void UpdateData() { EnterCriticalSection(&gCriticalSection); ...//do something LeaveCriticalSection(&gCriticalSection); } |
关于临界区的使用,有下列注意点:
(1)每个共享资源使用一个CRITICAL_SECTION变量;
(2)不要长时间运行关键代码段,当一个关键代码段长时间运行时,其他线程就会进入等待状态,这会降低应用程序的运行性能;
(3)如果需要同时访问多个资源,则可能连续调用EnterCriticalSection;
(4)Critical Section不是OS核心对象,如果进入临界区的线程"挂"了,将无法释放临界资源。这个缺点在Mutex中得到了弥补。
void AfxEnableControlContaine r( )
在应用程序对象的InitInstance函数中调用,使得程序支持包含OLE控件
在应用程序对象的InitInstance函数中调用,使得程序支持包含OLE控件
如果是跳出InitInstance()之后再运行自己的程序有两种情况:
1 如果是基于DOC/VIEW的,就在CView::OnInitUpdate()函数里执行一个自定义函数
2 如果是基于对话框的,就在CDialog::OnInitDialgo()函数里执行一个自定义函数
1
2
CWinApp::InitInstance
virtual BOOL InitInstance( );
返回值
如果初始化成功,则返回非零值;否则返回0。
注释
Windows允许在同一时刻运行程序的几份拷贝。在概念上,应用程序的初始化可以被分为两个部分:一次性的应用程序初始化工作,这些在应用程序第一次运行时完成,以及实例的初始化工作,每次运行程序的一个拷贝时都会执行这些操作,包括第一次运行时。框架中WinMain实现调用这个函数。
重载InitInstance以初始化在Windows下运行的应用程序的每个新实例。通常,你重载InitInstance以构造主窗口对象并设置CWinThread::m_pMainWnd数据成员,使其指向这个窗口。有关重载这个成员函数的更多信息参见“Visual C++ 程序员指南”中的“CWinApp:应用程序类”。