vc线程、信号量、Event对象的使用

注:实习期间的关于多线程方面的笔记,只是自己的理解,不一定权威正确。请读者慎重,请谅解。

一、

MFC多线程

可以通过AfxBeginThread创建用户界面线程或者工作者线程

工作者线程例子:

AfxBeginThread函数说明:

AfxBeginThread( AFX_THREADPROC pfnThreadProc,   //启动的函数
               LPVOID pParam,   //pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
               int nPriority = THREAD_PRIORITY_NORMAL, //0 设置优先级为正常。
               UINT nStackSize = 0, //指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
               DWORD dwCreateFlags , //指定创建线程以后,线程有怎么样的标志.可以指定两个值CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用:ResumeThread;0 : 创建线程后就开始运行.
               LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); //如果为 NULL,那么新创建的线程就具有和主线程一样的安全性.

创建线程:

CWinThread *m_MyThread;   //线程

m_MyThread=AfxBeginThread((AFX_THREADPROC)ThreadProc, NULL,0,0,CREATE_SUSPENDED,NULL);
//线程结束时不自动删除,才能获得lpExitCode,默认是TRUE
m_MyThread->m_bAutoDelete = FALSE;
m_MyThread->ResumeThread();

线程定义:

//线程函数

UINT WINAPI ThreadProc(LPVOID lpParameter)

{

while(1)

{

//可以通过Event信号对象或者bool值信号判断线程关闭条件

if(WaitForSingleObject(g_Dev.m_ThreadEvent, 0) == WAIT_OBJECT_0)

{

AfxEndThread(0);  //关闭线程,后面的就不执行了

//break;

}

Sleep(10);

。。。。。

}        

 

return0;

}


二、

获取线程结束代码

调用GetExitCodeThread()(并给予CreateThread所获得的线程handle作为参数)而得知:

 

BOOL GetExitCodeThread(

  HANDLE hThread,  //某线程

  LPDWORD lpExitCode  //线程退出代码

);

hThread:由CreateThread()传回的线程handle

lpExitCode:用于存储线程的返回值(exitcode)

返回值:该函数执行成功则返回非0值,否则返回 0(FALSE)

用法:

定义  DWORD lpExitCode=0;

线程函数定义如下:

DWORD WINAPI ThreadFunc(LPVOID n)

{

    Sleep((DWORD)n*1000*2);

    return 100;

}

结束线程如下:

AfxEndThread(lpExitCode);  //关闭线程

 

  1. for(;;)  
  2.   {  
  3.       BOOL rc;  
  4.       rc=GetExitCodeThread(hThrd1,&lpExitCode); 
  5.       if(rc&&exitCode!=STILL_ACTIVE)  
  6.           break;  
  7.   }


注解:

if( lpExitCode== STILL_ACTIVE )  //STILL_ACTIVE表示线程在运行

           trace("Thread1 is still running!");

if( lpExitCode!= STILL_ACTIVE ) //线程已经结束

或者

if( lpExitCode== 100)  //100为前面线程函数的返回值return


附:

CloseHandle()函数

线程的handle用处:

线程的handle是指向“线程的内核对象”的,而不是指向线程本身.每个内核对象只是内核分配的一个内存块,并且只能由内核访问。该内存块是一种数据结构,它的成员负责维护对象的各种信息(eg: 安全性描述,引用计数等)。

CloseHandle()

    在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。    但是这个handle并不能完全代表这个线程,它仅仅是线程的一个“标识”,系统和用户可以利用它对相应的线程进行必要的操纵。如果在线程成功创建后,不再需要用到这个句柄,就可以在创建成功后,线程退出前直接CloseHandle掉,但这并不会影响到线程的运行。关闭句柄后,线程并没关闭。

不执行CloseHandle() 带来的后果:

    若在线程执行完之后,没有通过CloseHandle()将引用计数减1,在进程执行期间,将会造成内核对象的泄露,相当与句柄泄露,但不同于内存泄露, 这势必会对系统的效率带来一定程度上的负面影响。但是,因为CWinThread::m_bAutoDelete缺省是TRUE,不需要显式CloseHandle()。但这是一个良好的编程习惯!


结束线程函数

TerminateThread(),ExitThread()的区别

TerminateThread()

函数的声明如下:

BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);
作用:
在线程外终止一个线程,用于强制终止线程。
参数说明:
HANDLE htread:被终止的线程的句柄,为CWinThread指针。
DWORD dwExitCode:退出码。
返回值:
函数执行成功则返回非零值,执行失败返回0。调用getlasterror获得返回的值。

ExitThread是比TerminateThread()好的一个结束线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除工作。

若要终止线程的运行,可以使用下面四种的方法:
    线程函数退出循环来返回(最佳方法 )。最佳
    通过调用ExitThread 函数,线程将自行撤消(尽量不要使用这种方法 )。 次之
    同一个进程或另一个进程中的线程调用TerminateThread 函数(最好避免使用这种方法 )。 次之
    该线程的主进程终止运行(避免使用 )。
使用线程函数返回:
    始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是确保所有线程资源被正确地清除的唯一办法。
如果线程能够返回,就可以确保下列事项的实现:
a) 在线程函数中创建的所有C + +对象均将通过它们的撤消函数正确地撤消。
b)操作系统将正确地释放线程堆栈使用的内存。
c)系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
d)系统将递减线程内核对象的使用计数。

在进程终止运行时撤消线程:
    ExitProcess和TerminateProcess函数也可以用来终止线程的运行。差别在于这些线程将会使终止运行的进程中的所有线程全部终止运行。另外,由于整个进程已经被关闭,进程使用的所有资源肯定已被清除。这当然包括所有线程的堆栈。这两个函数会导致进程中的剩余线程被强制撤消,就像从每个剩余的线程调用TerminateThread 一样。显然,这意味着正确的应用程序清除没有发生,即C++对象撤消函数没有被调用,数据没有转至磁盘等等。


三、

WaitForSingleObject 的用法

DWORD   WaitForSingleObject

(

       HANDLE hHandle,

       DWORDdwMilliseconds                                

);

参数hHandle 是一个某事件的句柄,第二个参数 dwMilliseconds 是时间间隔。如果事件A是有信号状态,则返回,返回WAIT_OBJECT_0 ,如果时间超过 dwMilliseconds 值,但事件还是无信号状态,则返回,返回 WAIT_TIMEOUT 。

用法:

//一旦事件对象处于有信号状态立即break

if(::WaitForSingleObject(g_End, 0) == WAIT_OBJECT_0)

{

break;

}

用法二、

WaitForSingleObject(g_event,INFINITE);

     For(;;)

        {

         ………… .

 

        }

 

该线程中,只有g_event变为有信号状态时才执行下面的 for 循环,若g_event 是全局变量,我们可以在别的线程中通过 g_event. SetEvent 控制这个线程。


四、

Event对象

函数原型:

  HANDLE CreateEvent(

    LPSECURITY_ATTRIBUTES lpEventAttributes,//安全属性,NULL为默认的安全属性

    BOOL bManualReset,//复位方式,TRUE为手动复位,FALSE为自动复位

    BOOL bInitialState,//初始状态,TRUE为初始为有信号,FALSE为无信号

    LPCTSTR lpName // 对象名称

  );

 



你可能感兴趣的:(C++/VC)