学习多线程之一:线程通信--利用事件对象
作者:惠州市东晓电子有限公司 龚辉斌

下载本文示例代码

在线程之间传送信号可以使用事件对象,用MFCCEvent来表示。一个事件对象有两种状态:信号态与非信号态。线程能监视于信号态的事件,以便在适当的时间完成对事件的操作。


创建事件的语句如下:

CEvent ThreadBegin;

事件被创建后就自动处于非信号态,要使用它处于信息态,必须使用对件对象的成员函数SetEvent(),即:

ThreadBegin.SetEvent();

线程监视这个信号来知道事件已准备就绪,从而可以进行其它的操作。而线程通过调用API函数WaitForSingleObject()来监视这个信号。

::WaitForSingleObject(ThreadBegin.m_hObject,INFINITE);

该函数的两个参数分别表示要检查的事件句柄与函数等待事件时间。

INFINITE
告诉WaitForSingleObject()在特定的事件不处于信号态时不要返回,即把它放在线程的开始的话,那么系统将挂起这个线程,直到事件处于信号态时才继续执行下去。当已经准备好线程继续执行下去的条件时,调用SetEvent(),当线程获得了相应信号态信号后便会继续执行下去。

一旦线程不再被挂起时,它便可以完成相关事务。但若要在主程序中通过线程结束,那么线程必须监视接下来的这个事件,看它是否处于信号态。线程可以通过对事件进行轮询,这时只要调用WaitForSingleObject()即可,只是等待时间参数的值被置为0,即:

::WaitForSingleObject(ThreadBegin.m_hObject,0);

当返回WAIT_OBJECT_0时,表示事件处于信号态,否则处于非信号态。

UINT ThreadProc(LPVOID param)

{

        ::WaitForSingleObject(ThreadBegin.m_hObject,INFINITE);

        ::MessageBox((HWND)param,"Thread Activated!","Thread",MB_OK);

 

        BOOL KeepRunning=true;

        while (KeepRunning)

        {

               int Result=::WaitForSingleObject(ThreadEnd.m_hObject,0);

               if (Result==WAIT_OBJECT_0)

                       KeepRunning=false;

        }

 

        ::MessageBox((HWND)param,"Thread Stopped!","Thread",MB_OK);

        return 0;

}


启动线程则用如下代码:

HWND hWnd=GetSafeHwnd();

AfxBeginThread(ThreadProc,hWnd);

ThreadBegin.SetEvent();

本文即本人在学习多任务多线程过程的手记,供大家参考,望能得到各位指点。
作者信箱:devvy@21cn.com

 

学习多线程之二:线程同步--使用临界区
作者:惠州市东晓电子有限公司 龚辉斌

下载本文示例源代码

临界区是一种保证在某一时刻只有一个线程能访问数据的简便办法。不管哪一个线程占用临界区对象,它都可以访问受保护的数据,而其他线程就必须等待,直到占用临界区的线程进行释放操作,从而临界区的数据是不会一个以上的线程同一时刻访问的。

MFC中通过CCtiticalSection类来创建临界区实例,如:

CCriticalSection CriticalSection;

当线程准备访问临界区的数据时,必须调用它的成员函数Lock()进行锁定,如:

CriticalSecion.Lock();

如果没有任何线程占用临界区,Lock()可以向调用纯种提供临界区数据的访问,当线程完成各项数据操作后,再调用成员函数UnLock()进行释放,别的线程才可以占用临界区。如:

CriticalSection.UnLock();

可以定义一个数据实例:

class CDataArray

{

private:

        int iArray[10];

        CCriticalSection CriticalSection;

public:

        CDataArray(){};

        ~CDataArray(){};

 

    void SetData(int iValue);

    void GetDataArray(int aArray[10]);

};

在头文件中必须包含afxmt.h,它提供了程序对CCriticalSection的访问机制。

void CDataArray::SetData(int iValue)

{

        CriticalSection.Lock();

        for (int i=0;i<10;i++)

               iArray[i]=iValue;

        CriticalSection.Unlock();

}

 

void CDataArray::GetDataArray(int aArray[10])

{

        CriticalSection.Lock();

        for (int i=0;i<10;i++)

               aArray[i]=iArray[i];

        CriticalSection.Unlock();

}

增加数据实例:

CDataArray DataArray;

再编写读写函数:

UINT Thread_WriteProc(LPVOID param)

{

        for (int i=0;i<10;i++)

        {

               DataArray.SetData(i);

               ::Sleep(500);

        }

        return 0;

}

 

UINT Thread_ReadProc(LPVOID param)

{

        int aArray[10];

        for (int i=0;i<20;i++)

        {

               DataArray.GetDataArray(aArray);

               char str[255];

               str[0]=0;

               for (int j=0;j<10;j++)

               {

                       int len=strlen(str);

                       wsprintf(&str[len],"%d",aArray[j]);

               }

               ::MessageBox((HWND)param,str,"Thread Read Proc",MB_OK);

        }

        return 0;

}

这样我们可以启动这两个线程函数:

HWND hWnd=GetSafeHwnd();

AfxBeginThread(Thread_WriteProc,hWnd);

AfxBeginThread(Thread_ReadProc,hWnd);

这样当Thread_WriteProc占用临界区时,Thread_ReadProc必须等待,直到Thread_WriteProc退出了临界区,从而实现线程间的同步。
本文即本人在学习多任务多线程过程的手记,供大家参考,望能得到各位指点。 作者信箱:devvy@21cn.com

 

学习多线程之三:线程同步--使用互斥
作者:惠州市东晓电子有限公司 龚辉斌

下载本文配套代码

互斥跟临界区很相似,便远比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。通过CMutex来完成线程间的互质,即:CMutex Mutext;

从而我们可以这样来定义数据对象:

#include "afxmt.h"

 

class CDataArray

{

private:

        int iArray[10];

    CMutex Mutex;

public:

    CDataArray(){};

    ~CDataArray(){};

 

    void SetData(int iValue);

    void GetDataArray(int aArray[10]);

};

成员函数实现如下:

void CDataArray::SetData(int iValue)

{

        CSingleLock SingleLock(&Mutex);

        SingleLock.Lock();

        for (int i=0;i<10;i++)

               iArray[i]=iValue;

}

 

void CDataArray::GetDataArray(int aArray[10])

{

        CSingleLock SingleLock(&Mutex);

        SingleLock.Lock();

        for (int i=0;i<10;i++)

               aArray[i]=iArray[i];

}

为了访问一个互斥对象,务必建立一个CSingleLockCMultiLock对象,用于访问控制。如果互斥没有被线程占用,那么当前的调用线程可以成为互斥的占用者。要实现对互斥的访问,就要调用CSingleLock的成员函数Lock(),即:
SingleLock.Lock();
如果一个线程占用了互斥,那么系统将挂起当前的调用线程,直到这个互斥被释放为止,这时,被挂起的线程将被唤醒并取得对互斥的控制。
释放互斥是通过调用CSingleLock的成员函数UnLock()来实现的。CDataArray的成员函数在退出时,将自动进行解锁操作。因为CSingleLock被创建在椎栈上,系统自动完成对UnLock()的调用。

本文即本人在学习多任务多线程过程的手记,供大家参考,望能得到各位指点。
作者信箱:devvy@21cn.com

学习多线程之四:线程同步--使用信号量
作者:惠州市东晓电子有限公司 龚辉斌

下载本文配套代码

除了使用临界区与互斥可以完成线程间的同步外,还可以使用信号量CSemaphore。使用信号量还有一个好处便是:信号允许多个线程同时使用共享资源,这便与操作系统中的PV操作有些雷同的地方。它指出了同时访问共享资源的线程最大数目。

在信号量内部有一个计数器,当有线程访问共享资源时,计数器将自动递减,当它为0时,不再允许其他线程对共享资源的访问,直到有一个线程释放共享资源,从而完成对共享资源的保护。



在建立一个信号量时必须提供一个初始化值和一个最大计数值,如:

CSemaphore Semaphore(2,2);

可以在类的构造函数中动态地创建CSemaphore对象,如:

Semaphore=new CSemaphore(2,2);

信号量CSemaphore被建立后,就可以准备用它来对共享资源进行访问计数。要完成计数处理,首先应建立一个CSingleLockCMultiLock对象,如:

CSingleLock SingleLock(Semaphore);

要减小这个信号Semaphore的计数值,只须调用CSingleLock对象的成员函数Lock()即可:

SingleLock.Lock();

同样,通过调用UnLock()来释放这个信号量,即:

SingleLock.UnLock();

从而我们可这样来申明类:

#include "afxmt.h"

 

class CSharedResource

{

private:

        CSemaphore* ptrSemaphore;

public:

        CSharedResource();

        ~CSharedResource();

 

        void AccessResource();

};

在构造函数中完成对信号对象的创建:

CSharedResource::CSharedResource()

{

        ptrSemaphore=new CSemaphore(2,2);

}

在析构函数中完成对信号对象的释放:

CSharedResource::~CSharedResource()

{

        delete ptrSemaphore;

}

用以下过程来访问共享资源:

void CSharedResource::AccessResource()

{

        CSingleLock SingleLock(ptrSemaphore);

        SingleLock.Lock();

        /* ......

          资源访问

        */

        Sleep(1000);

}

再建立使用信号量的线程函数:

UINT Thread_Proc1(LPVOID param)

{

        SharedResource.AccessResource();

        ::MessageBox((HWND)param,"Thread #1 had accessed the Semaphore!","Thread Proc1",MB_OK);

        return 0;

}

 

UINT Thread_Proc2(LPVOID param)

{

        SharedResource.AccessResource();

        ::MessageBox((HWND)param,"Thread #2 had accessed the Semaphore!","Thread Proc2",MB_OK);

        return 0;

}

 

UINT Thread_Proc3(LPVOID param)

{

        SharedResource.AccessResource();

        ::MessageBox((HWND)param,"Thread #3 had accessed the Semaphore!","Thread Proc3",MB_OK);

        return 0;

}

再用以下代码执行各个线程函数:

HWND hWnd=GetSafeHwnd();

AfxBeginThread(Thread_Proc1,hWnd);

AfxBeginThread(Thread_Proc2,hWnd);

AfxBeginThread(Thread_Proc3,hWnd);

这样当Thread_Proc1Thread_Proc2占用信号量之后,只有等其中一个释放信号量之后,Thread_Proc3方可占用信号量,达到了线程之间资源的保护,实现数据共享!

本文即本人在学习多任务多线程过程的手记,供大家参考,望能得到各位指点。
作者信箱:devvy@21cn.com