??xml version="1.0" encoding="utf-8" standalone="yes"?>
一?实现Ҏ(gu)
1、理解线E?br>
要讲解线E,不得不说一下进E,q程是应用程序的执行实例Q每个进E是q有的虚拟地址I间、代码、数据和其它pȝ资源l成。进E在q行时创建的资源
随着q程的终止而死亡。线E的基本思想很简单,它是一个独立的执行,是进E内部的一个独立的执行单元Q相当于一个子E序Q它对应于Visual
C++中的CwinThreadcd象。单独一个执行程序运行时Q缺省地包含的一个主U程Q主U程以函数地址的Ş式出玎ͼ提供E序的启动点Q如main
Q)(j)或WinMainQ)(j)函数{。当ȝE终止时Q进E也随之l止。根据实际需要,应用E序可以分解成许多独立执行的U程Q每个线Eƈ行的q行在同一q程
中?br>
一个进E中的所有线E都在该q程的虚拟地址I间中,使用该进E的全局变量和系l资源。操作系l给每个U程分配不同的CPU旉片,在某一个时刻,
CPU只执行一个时间片内的U程Q多个时间片中的相应U程在CPU内轮执行,׃每个旉片时间很短,所以对用户来说Q仿?jng)各个线E在计算Z是ƈ行处
理的。操作系l是Ҏ(gu)U程的优先来安排CPU的时_(d)优先U高的线E优先运行,优先U低的线E则l箋{待?br>
U程被分ZU:(x)用户界面U程和工作线E(又称为后台线E)(j)。用L(fng)面线E通常用来处理用户的输入ƈ响应各种事g和消息,其实Q应用程序的L行线E?
CWinAPP对象是一个用L(fng)面线E,当应用程序启动时自动创徏和启动,同样它的l止也意味着该程序的l束Q进E终止。工作线E用来执行程序的后台?
理Q务,比如计算、调度、对串口的读写操作等Q它和用L(fng)面线E的区别是它不用从CWinThreadcL生来创徏Q对它来说最重要的是如何实现工作U程
d的运行控制函数。工作线E和用户界面U程启动时要调用同一个函数的不同版本Q最后需要读者明白的是,一个进E中的所有线E共享它们父q程的变量,但同
时每个线E可以拥有自q变量?
2、线E的理和操?br>
Q一Q线E的启动
创徏一个用L(fng)面线E,首先要从cCwinThread产生一个派生类Q同时必M用DECLARE_DYNCREATE?
IMPLEMENT_DYNCREATE来声明和实现q个CwinThreadzcR第二步是根据需要重载该zcȝ一些成员函数如Q?
ExitInstanceQ)(j)、InitInstanceQ)(j)、OnIdleQ)(j)、PreTranslateMessageQ)(j){函数。最后调?
AfxBeginThread()函数的一个版本:(x)CWinThread* AfxBeginThread( CRuntimeClass*
pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize =
0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs =
NULL )
启动该用L(fng)面线E,其中W一个参Cؓ(f)指向定义的用L(fng)面线E类指针变量Q第二个参数为线E的优先U,W三个参Cؓ(f)U程所对应的堆栈大,W四个参Cؓ(f)U?
E创建时的附加标志,~省为正常状态,如ؓ(f)CREATE_SUSPENDED则线E启动后为挂L(fng)态?br>
对于工作U程来说Q启动一个线E,首先需要编写一个希望与应用E序的其余部分ƈ行运行的函数如Fun1()Q接着定义一个指向CwinThread?
象的指针变量*pThread,调用AfxBeginThread(Fun1,param,priority)函数Q返回DlpThread变量的同?
一q启动该U程来执行上面的Fun1Q)(j)函数Q其中Fun1是线E要q行的函数的名字Q也既是上面所说的控制函数的名字,param是准备传送给U程函数
Fun1的Q?2位|priority则是定义该线E的优先U别Q它是预定义的常敎ͼ读者可参考MSDN?br>
Q二Q线E的优先U?br>
以下的CwinThreadcȝ成员函数用于U程优先U的操作Q?br>
////////////////////////////////////////////////////////////////
//////CtestView message handlers
/////Set to True to end thread
Bool bend=FALSE;//定义的全局变量Q用于控制线E的q行Q?br>
//The Thread FunctionQ?br>
UINT ThreadFunction(LPVOID pParam)//U程函数
{
while(!bend)
{
Beep(100,100);
Sleep(1000);
}
return 0;
}
/////////////////////////////////////////////////////////////
CwinThread *pThread;
HWND hWnd;
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
pThread=AfxBeginThread(ThradFunction,hWnd);//启动U程
pThread->m_bAutoDelete=FALSE;//U程为手动删?br>
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{
bend=TRUE;//改变变量Q线E结?br>
WaitForSingleObject(pThread->m_hThread,INFINITE);//{待U程l束
delete pThread;//删除U程
Cview::OnDestroy();
}
3、线E之间的通信
通常情况下,一个次U线E要ZU程完成某种特定cd的Q务,q就隐含着表示在主U程和次U线E之间需要徏立一个通信的通道。一般情况下Q有下面的几
U方法实现这U通信dQ用全局变量Q上一节的例子其实使用的就是这U方法)(j)、用事件对象、用消息。这里我们主要介l后两种Ҏ(gu)?br>
Q一Q?利用用户定义的消息通信
在WindowsE序设计中,应用E序的每一个线E都拥有自己的消息队列,甚至工作U程也不例外Q这样一来,׃得线E之间利用消息来传递信息就变的
非常单。首先用戯定义一个用h息,如下所C:(x)#define WM_USERMSG
WMUSER+100Q在需要的时候,在一个线E中调用Q:(x)PostMessage((HWND)param,WM_USERMSG,0,0)?
CwinThread::PostThradMessageQ)(j)来向另外一个线E发送这个消息,上述函数的四个参数分别是消息要发送到的目的窗口的?
柄、要发送的消息标志W、消息的参数WPARAM和LPARAM。下面的代码是对上节代码的修改,修改后的l果是在U程l束时显CZ个对话框Q提C线E结
束:(x)
Retrun 0;
}
上面的例子是工作者线E向用户界面U程发送消息,对于工作者线E,如果它的设计模式也是消息驱动的,那么调用者可以向它发送初始化、退
出、执行某U特定的处理{消息,让它在后台完成。在控制函数中可以直接用:(x)QGetMessage()q个SDK函数q行消息分检和处理,自己实现一?
消息循环。GetMessage()函数在判断该U程的消息队列ؓ(f)I时Q线E将pȝ分配l它的时间片让给其它U程Q不无效的占用CPU的时_(d)如果消息?
列不为空Q就获取q个消息Q判断这个消息的内容q进行相应的处理?br>
Q二Q用事g对象实现通信
在线E之间传递信可行通信比较复杂的方法是使用事g对象Q用MFC的Ceventcȝ对象来表C。事件对象处于两U状态之一Q有信号和无信号Q线E可以监视处于有信号状态的事gQ以便在适当的时候执行对事g的操作。上qC子代码修改如下:(x)
while(!bend)
{
Beep(100,100);
Sleep(1000);
Int result=::WaitforSingleObject(threadEnd.m_hObject,0);
//{待threadEnd事g有信P无信hU程在这里?zhn)?br>
If(result==Wait_OBJECT_0)
Bend=TRUE;
}
Q:(x)PostMessage(hWnd,WM_USERMSG,0,0)Q?br>
return 0;
}
/////////////////////////////////////////////////////////////
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
threadStart.SetEvent();//threadStart事g有信?br>
pThread=AfxBeginThread(ThreadFunction,hWnd);//启动U程
pThread->m_bAutoDelete=FALSE;
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{
threadEnd.SetEvent();
WaitForSingleObject(pThread->m_hThread,INFINITE);
delete pThread;
Cview::OnDestroy();
}
q行q个E序Q当关闭E序Ӟ才显C提C框Q显C?Thread ended"?
4、线E之间的同步
前面我们讲过Q各个线E可以访问进E中的公共变量,所以用多U程的过E中需要注意的问题是如何防止两个或两个以上的线E同时访问同一个数据,以免?
坏数据的完整性。保证各个线E可以在一起适当的协调工作称为线E之间的同步。前面一节介l的事g对象实际上就是一U同步Ş式。Visual
C++中用同步类来解x作系l的q行性而引L(fng)数据不安全的问题QMFC支持的七个多U程的同步类可以分成两大c:(x)同步对象
QCsyncObject、Csemaphore、Cmutex、CcriticalSection和CeventQ和同步讉K对象
QCmultiLock和CsingleLockQ。本节主要介l(f)界区Qcritical
sectionQ、互斥(mutexeQ、信号量QsemaphoreQ,q些同步对象使各个线E协调工作,E序q行h更安全?br>
Q一Q?临界?br>
临界区是保证在某一个时间只有一个线E可以访问数据的Ҏ(gu)。用它的过E中Q需要给各个U程提供一个共享的临界区对象,无论哪个U程占有临界区对象,
都可以访问受C护的数据Q这时候其它的U程需要等待,直到该线E释放(f)界区对象为止Q(f)界区被释攑Q另外的U程可以强占q个临界区,以便讉K׃n的数
据。(f)界区对应着一个CcriticalSection对象Q当U程需要访问保护数据时Q调用(f)界区对象的Lock()成员函数Q当对保护数据的操作完成
之后Q调用(f)界区对象的Unlock()成员函数释放对(f)界区对象的拥有权Q以使另一个线E可以夺取(f)界区对象q访问受保护的数据。同时启动两个线E,?
们对应的函数分别为WriteThread()和ReadThread()Q用以对公共数组larray[]操作Q下面的代码说明了如何用(f)界区对象Q?br>
int array[10],destarray[10];
CCriticalSection Section;
UINT WriteThread(LPVOID param)
{
Section.Lock();
for(int x=0;x<10;x++)
array[x]=x;
Section.Unlock();
}
UINT ReadThread(LPVOID param)
{
Section.Lock();
For(int x=0;x<10;x++)
Destarray[x]=array[x];
Section.Unlock();
}
上述代码q行的结果应该是Destarray数组中的元素分别?-9Q而不是杂乱无章的敎ͼ如果不用同步,则不是这个结果,有兴的读者可以实验一下?br>
Q二Q互?br>
互斥与(f)界区很相|但是使用时相对复杂一些,它不仅可以在同一应用E序的线E间实现同步Q还可以在不同的q程间实现同步,从而实现资源的安全׃n?
互斥与Cmutexcȝ对象相对应,使用互斥对象Ӟ必须创徏一个CSingleLock或CMultiLock对象Q用于实际的讉K控制Q因里的?
子只处理单个互斥Q所以我们可以用CSingleLock对象Q该对象的Lock()函数用于占有互斥QUnlock()用于释放互斥。实C码如下:(x)
int array[10],destarray[10];
CMutex Section;
UINT WriteThread(LPVOID param)
{
CsingleLock singlelock;
singlelock (&Section);
singlelock.Lock();
for(int x=0;x<10;x++)
array[x]=x;
singlelock.Unlock();
}
UINT ReadThread(LPVOID param)
{
CsingleLock singlelock;
singlelock (&Section);
singlelock.Lock();
For(int x=0;x<10;x++)
Destarray[x]=array[x];
singlelock.Unlock();
}
Q三Q信号量
信号量的用法和互斥的用法很相|不同的是它可以同一时刻允许多个U程讉K同一个资源,创徏一个信号量需要用Csemaphorecd明一个对象,一
旦创Z一个信号量对象Q就可以用它来对资源的访问技术。要实现计数处理Q先创徏一个CsingleLock或CmltiLock对象Q然后用该对象的
Lock()函数减少q个信号量的计数|Unlock()反之。下面的代码分别启动三个U程Q执行时同时昄二个消息框,然后10U后W三个消息框才得
以显C?br>
return 0;
}
UINT ThreadProc2(LPVOID param)
{
CSingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread2 had access","Thread2",MB_OK);
return 0;
}
UINT ThreadProc3(LPVOID param)
{
CsingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread3 had access","Thread3",MB_OK);
return 0;
}
二?~程步骤
1?启动Visual C++6.0Q生成一?2位的控制台程序,该E序命名?sequence"
2?输入要排l的数字Q声明四个子U程Q?br>
3?输入代码Q编译运行程序?/p>
三?E序代码
#include "stdlib.h"
#include "memory.h"
HANDLE evtTerminate; //事g信号,标记是否所有子U程都执行完
/*
下面使用了三U控制方法,你可以注释其中两U,使用其中一U?br>
注意修改时要q带修改临界区PrintResult里的相应控制语句
*/
HANDLE evtPrint; //事g信号,标记事g是否已发?br>
//CRITICAL_SECTION csPrint; //临界?br>
//HANDLE mtxPrint; //互斥信号,如有信号表明已经有线E进入(f)界区q拥有此信号
static long ThreadCompleted = 0;
/*用来标记四个子线E中已完成线E的个数,当一个子U程完成时就对ThreadCompletedq行加一操作,
要用InterlockedIncrement(long* lpAddend)和InterlockedDecrement(long*
lpAddend)q行加减操作*/
//下面的结构是用于传送排序的数据l各个排序子U程
struct MySafeArray
{
long* data;
int iLength;
};
//打印每一个线E的排序l果
void PrintResult(long* Array, int iLength, const char* HeadStr = "sort");
//排序函数
unsigned long __stdcall BubbleSort(void* theArray); //冒(chng)排序
unsigned long __stdcall SelectSort(void* theArray); //选择排序
unsigned long __stdcall HeapSort(void* theArray); //堆排?br>
unsigned long __stdcall InsertSort(void* theArray); //插入排序
/*以上四个函数的声明必适合作ؓ(f)一个线E函数的必要条g才可以用CreateThread
建立一个线E?br>
Q?Q调用方法必L__stdcallQ即函数参数压栈序由右到左Q而且由函数本w负?br>
栈的恢复, C和C++默认是__cdecl, 所以要昑ּ声明是__stdcall
Q?Q返回值必Lunsigned long
Q?Q参数必L一?2位|如一个指针值或longcd
(4) 如果函数是类成员函数Q必d明ؓ(f)static函数Q在CreateThread时函数指针有Ҏ(gu)的写法。如?函数是类CThreadTest的成员函C)Q?br>
static unsigned long _stdcall MyThreadFun(void* pParam);
handleRet = CreateThread(NULL, 0, &CThreadTestDlg::MyThreadFun, NULL, 0, &ThreadID);
之所以要声明为static是由于,该函数必要独立于对象实例来使用Q即使没有声明实例也可以使用?/
int QuickSort(long* Array, int iLow, int iHigh); //快速排?br>
int main(int argc, char* argv[])
{
long data[] = {123,34,546,754,34,74,3,56};
int iDataLen = 8;
//Z对各个子U程分别对原始数据进行排序和保存排序l果
//分别分配内存对data数组的数据进行复?br>
long *data1, *data2, *data3, *data4, *data5;
MySafeArray StructData1, StructData2, StructData3, StructData4;
data1 = new long[iDataLen];
memcpy(data1, data, iDataLen << 2); //把data中的数据复制到data1?br>
//内存复制 memcpy(目标内存指针, 源内存指? 复制字节?, 因ؓ(f)long的长?br>
//?字节,所以复制的字节Cؓ(f)iDataLen << 2, 即等于iDataLen*4
StructData1.data = data1;
StructData1.iLength = iDataLen;
data2 = new long[iDataLen];
memcpy(data2, data, iDataLen << 2);
StructData2.data = data2;
StructData2.iLength = iDataLen;
data3 = new long[iDataLen];
memcpy(data3, data, iDataLen << 2);
StructData3.data = data3;
StructData3.iLength = iDataLen;
data4 = new long[iDataLen];
memcpy(data4, data, iDataLen << 2);
StructData4.data = data4;
StructData4.iLength = iDataLen;
data5 = new long[iDataLen];
memcpy(data5, data, iDataLen << 2);
unsigned long TID1, TID2, TID3, TID4;
//对信号量q行初始?br>
evtTerminate = CreateEvent(NULL, FALSE, FALSE, "Terminate");
evtPrint = CreateEvent(NULL, FALSE, TRUE, "PrintResult");
//分别建立各个子线E?br>
CreateThread(NULL, 0, &BubbleSort, &StructData1, NULL, &TID1);
CreateThread(NULL, 0, &SelectSort, &StructData2, NULL, &TID2);
CreateThread(NULL, 0, &HeapSort, &StructData3, NULL, &TID3);
CreateThread(NULL, 0, &InsertSort, &StructData4, NULL, &TID4);
//在主U程中执行行快速排序,其他排序在子U程中执?
QuickSort(data5, 0, iDataLen - 1);
PrintResult(data5, iDataLen, "Quick Sort");
WaitForSingleObject(evtTerminate, INFINITE); //{待所有的子线E结?br>
//所有的子线E结束后Q主U程才可以结?br>
delete[] data1;
delete[] data2;
delete[] data3;
delete[] data4;
CloseHandle(evtPrint);
return 0;
}
/*
冒(chng)排序思想(升序Q降序同理,后面的算法一样都是升?:从头到尾Ҏ(gu)据进行两两比较进行交?的攑։大的攑。这样一ơ下来,最大的元素׃(x)被交换的最后,然后下一?
循环׃用对最后一个元素进行比较交换了Q所以呢每一ơ比较交换的ơ数都比上一ơ@环的ơ数一Q这样Nơ之后数据就变得升序排列?/
unsigned long __stdcall BubbleSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
int i, j=0;
long swap;
for (i = iLength-1; i >0; i--)
{
for(j = 0; j < i; j++)
{
if(Array[j] >Array[j+1]) //前比后大Q交?br>
{
swap = Array[j];
Array[j] = Array[j+1];
Array[j+1] = swap;
}
}
}
PrintResult(Array, iLength, "Bubble Sort"); //向控制台打印排序l果
InterlockedIncrement(&ThreadCompleted); //q回前ɾU程完成数标记加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //(g)查是否其他线E都已执行完
//若都执行完则讄E序l束信号?br>
return 0;
}
/*选择排序思想:每一ơ都从无序的数据中找出最的元素Q然后和前面已经有序的元素序列的后一个元素进行交换,q样整个源序列就?x)分成两部分Q前面一?
分是已经排好序的有序序列Q后面一部分是无序的Q用于选出最的元素。@环Nơ之后,前面的有序序列加长到跟源序列一样长Q后面的无序部分长度变ؓ(f)0Q排
序就完成了?/
unsigned long __stdcall SelectSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
long lMin, lSwap;
int i, j, iMinPos;
for(i=0; i < iLength-1; i++)
{
lMin = Array[i];
iMinPos = i;
for(j=i + 1; j <= iLength-1; j++) //从无序的元素中找出最的元素
{
if(Array[j] < lMin)
{
iMinPos = j;
lMin = Array[j];
}
}
//把选出的元素交换拼接到有序序列的最?br>
lSwap = Array[i];
Array[i] = Array[iMinPos];
Array[iMinPos] = lSwap;
}
PrintResult(Array, iLength, "Select Sort"); //向控制台打印排序l果
InterlockedIncrement(&ThreadCompleted); //q回前ɾU程完成数标记加1
if(ThreadCompleted == 4) SetEvent(evtTerminate);//(g)查是否其他线E都已执行完
//若都执行完则讄E序l束信号?br>
return 0;
}
/*堆排序思想Q堆Q数据元素从1到N排列成一二叉树(wi)Q而且q棵?wi)的每一个子?wi)的栚w是该?wi)中的元素的最或最大的元素q样如果一个无序数据集合是一?
堆那么,根元素就是最或最大的元素堆排序就是不断对剩下的数据徏堆,把最或最大的元素析透出来。下面的法Q就是从最后一个元素开始,依据一个节Ҏ(gu)
父节Ҏ(gu)值大的原则对所有元素进行调_(d)q样调整一ơ就形成一个堆Q第一个元素就是最的元素。然后再对剩下的无序数据再进行徏堆,注意q时后面的无序数
据元素的序数都要改变Q如W一ơ徏堆后Q第二个元素׃(x)变成堆的W一个元素?/
unsigned long __stdcall HeapSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
int i, j, p;
long swap;
for(i=0; i {
for(j = iLength - 1; j>i; j--) //从最后倒数上去比较字节点和父节?br>
{
p = (j - i - 1)/2 + i; //计算父节Ҏ(gu)l下?br>
//注意到树(wi)节点序数跟数l下标不是等同的Q因为徏堆的元素个数逐个递减
if(Array[j] < Array[p]) //如果父节Ҏ(gu)值大则交换父节点和字节点
{
swap = Array[j];
Array[j] = Array[p];
Array[p] = swap;
}
}
}
PrintResult(Array, iLength, "Heap Sort"); //向控制台打印排序l果
InterlockedIncrement(&ThreadCompleted); //q回前ɾU程完成数标记加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //(g)查是否其他线E都已执行完
//若都执行完则讄E序l束信号?br>
return 0;
}
/*插入排序思想Q把源数据序列看成两半,前面一半是有序的,后面一半是无序的,把无序的数据从头到尾逐个逐个的插入到前面的有序数据中Q得有序的数据的个C断增大,同时无序的数据个数就来少Q最后所有元素都?x)变得有序?/
unsigned long __stdcall InsertSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
int i=1, j=0;
long temp;
for(i=1; i {
temp = Array[i]; //取出序列后面无序数据的第一个元素?br>
for(j=i; j>0; j--) //和前面的有序数据逐个q行比较扑և合适的插入位置
{
if(Array[j - 1] >temp) //如果该元素比插入值大则后U?br>
Array[j] = Array[j - 1];
else //如果该元素比插入值小Q那么该位置的后一位就是插入元素的位置
break;
}
Array[j] = temp;
}
PrintResult(Array, iLength, "Insert Sort"); //向控制台打印排序l果
InterlockedIncrement(&ThreadCompleted); //q回前ɾU程完成数标记加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //(g)查是否其他线E都已执行完
//若都执行完则讄E序l束信号?br>
return 0;
}
/*快速排序思想Q快速排序是分治思想的一U应用,它先选取一个支点,然后把小于支点的元素交换到支点的前边Q把大于支点的元素交换到支点的右辏V然后再Ҏ(gu)点左辚w分和?br>
辚w分进行同L(fng)处理Q这栯q次之后Q数据就?x)变得有序。下面的实现使用了递归
建立两个游标QiLowQiHighQiLow指向序列的第一个元素,iHigh指向最后一个先选第一个元素作为支点,q把它的值存贮在一个辅助变量里?
那么W一个位|就变ؓ(f)Iƈ可以攄其他的元素?
q样从iHigh指向的元素开始向前移动游标,iHigh查找比支点小的元素,如果扑ֈQ则把它攄到空|了的位|(现在是第一个位|)(j)Q然后iHigh
游标停止UdQ这时iHigh指向的位|被I置Q然后移动iLow游标L比支点大的元素放|到iHigh指向的空|的位置Q如此往复直到iLow?
iHigh相等。最后用递归对左右两部分q行同样处理*/
int QuickSort(long* Array, int iLow, int iHigh)
{
if(iLow >= iHigh) return 1; //递归l束条g
long pivot = Array[iLow];
int iLowSaved = iLow, iHighSaved = iHigh; //保未改变的iLowQiHighg存v?br>
while (iLow < iHigh)
{
while (Array[iHigh] >= pivot && iHigh >iLow) //L比支点大的元?br>
iHigh -- ;
Array[iLow] = Array[iHigh]; //把找到的元素攄到空|的位置
while (Array[iLow] < pivot && iLow < iHigh) //L比支点小的元?br>
iLow ++ ;
Array[iHigh] = Array[iLow]; //把找到的元素攄到空|的位置
}
Array[iLow] = pivot; //把支点值放|到支点位置Q这时支点位|是I置?br>
//对左右部分分别进行递归处理
QuickSort(Array, iLowSaved, iHigh-1);
QuickSort(Array, iLow+1, iHighSaved);
return 0;
}
//每一个线E都要用这个函数进行输出,而且只有一个显C器Q生多个线E?br>
//竞争Ҏ(gu)制台的用权?br>
void PrintResult(long* Array, int iLength, const char* HeadStr)
{
WaitForSingleObject(evtPrint, INFINITE); //{待事g有信?br>
//EnterCriticalSection(&csPrint); //标记有线E进入(f)界区
//WaitForSingleObject(mtxPrint, INFINITE); //{待互斥量空|(没有U程拥有它)(j)
int i;
printf("%s: ", HeadStr);
for (i=0; i {
printf("%d,", Array[i]);
Sleep(100); //延时Q可以去掉)(j)
/*只是使得多线E对临界问的问题比较Ҏ(gu)看得?br>
如果你把临界控制的语句注释掉Q输出就?x)变得很凌ؕQ各个排序的l果?br>
分插间隔着输出Q如果不延时׃Ҏ(gu)看到q种不对临界区控制的l果
*/
}
printf("%d\n", Array[i]);
SetEvent(evtPrint); //把事件信号量恢复Q变为有信号
}
四?结
对复杂的应用E序来说Q线E的应用l应用程序提供了高效、快速、安全的数据处理能力。本实例讲述了线E处理中l常遇到的问题,希望对读者朋友有一定的帮助Qv到抛砖引玉的作用?/p>
转自Qhttp://hi.baidu.com/laodun/blog/item/8ab8f3241af3f7318644f916.htmlQ?/p>
1. 怎样使用MFC发送一个消息用MFC发送一个消息的Ҏ(gu)是,
首先Q应获取接收消息的CWndcd象的指针Q?br /> 然后,调用CWnd的成员函数SendMessage( )?br /> LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
pWnd指针指向目标CWndcd象。变量Msg是消息,wParam和lParam变量包含消息的参敎ͼ如鼠标单d里或选择了什么菜单项。目标窗口返回的消息l果攑֜变量Res中?br /> 发送消息到一个没有CWndcd象的H口Q可以用下列目标H口的句柄直接调用Windows APIQ?br /> LRESULT Res=::SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
q里的hWnd是目标窗口的句柄?br />2. 怎样用MFC寄送一个消?br /> 用MFC寄送一个消息与发送一个消息几乎相同,但寄送时用PostMessage( ) Q而不是用SendMessage( )Q返回值Res也不一PRes不是一个由目标H口q回的|而是一个布?yu)(dng)|用来表示消息是否成功地放到消息队列中?br />3. (g)索一个寄送消?br /> 正常情况下,一旦消息被寄送后Q应用程序在后台发送它。但是在Ҏ(gu)情况下,需要你自己d除一个消息,例如惛_应用E序接收到某U消息之前停止应用程序。有两种Ҏ(gu)可以从应用程序消息队列中删除一个消息,但这两种Ҏ(gu)都没有涉?qing)MFC?br />?W一U方法:(x)在不q扰M事情之下H视消息队列Q看看一个消息是否在那里?br /> BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ;
?W二U方法:(x)实际上是{待Q一直等C个新的消息到N列ؓ(f)止,然后删除q返回该消息?br /> BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
在这两种Ҏ(gu)中,变量hWnd指定要截h息的H口Q如果该变量设ؓ(f)NULLQ所有窗口消息将被截莗wMsgFilterMin和wMsgFilterMax变量与SendMessage( )中的变量Msg相对应,指定查看消息的范围。如果用"0,0"Q则所有的消息都将被截莗如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELASTQ则所有键盘或鼠标的消息将被截莗wRemoveMsg变量指定PeekMessage( )是否应该真正C队列中删除该消息?GetMessage( )L删除消息)。该变量可以取两个|(x)
?PM_REMOVEQPeekMessage( )删除消息?br /> ?PM_NOREMOVEQPeekMessage( )把消息留在队列里,q返回它的一个拷贝?br /> 当然Q如果把消息留在消息队列中,然后再次调用PeekMessage( )查看相同cd的消息,则将q回完全相同的消息?br /> lpMsg变量是一个指向MSGl构的指针,MSG包含(g)索到的消息?br /> typedef struct tagMSG {
HWND hwnd; // window handle message is intended for
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time; // the time the message was put in the queue
POINT pt; // the location of the mouse cursor when the
// message was put in the queue
} MSG;
4. MFC怎样接收一个寄送的消息
MFC处理一个寄送和发送消息的唯一明显不同是寄送的消息要在应用E序的消息队列中p一些时间。在消息?message pump)弹出它之前,它要一直在队列中?br /> 消息?br /> MFC应用E序中的消息泵在CWinApp的成员函数Run()中。应用程序开始运行时QRun()p调用,Run()把时间分割成两部分。一部分用来执行后台处理Q如取消临时CWnd对象Q另一部分用来(g)查消息队列。当一个新的消息进来时QRun()抽取它—即用GetMessage( )从队列中取出该消息,q行两个消息译函数Q然后用DispatchMessage( )函数调用该消息预期的目标H口q程?br /> 消息泵调用的两个译函数是PreTranslateMessage( )?:TranslateMessage( )。目标窗口的MFCcd调用reTranslateMessage在发送消息给它之前进行消息翻译,例如QCFrameWnd用PreTranslateMessage( )加速键(如,Ctrl+S存储文g)转换为命令消息。翻译前的消息通常被处理掉Q而翻译后的消?如果有的?被重新寄送到队列里?:TranslateMessage是一个窗口函敎ͼ原始键码{换ؓ(f)键字W。消息一旦被DispatchMessage()发送,MFC处理它就像处理SendMessage()发送的消息一栗?br />5. MFC怎样处理一个接收到的消?br /> 处理接收到的消息的目的非常简单:(x)消息指向一个函敎ͼ该函数通过消息中的消息标识W处理它。非MFCH口用简单的case语句来实现该目标Q每个case语句执行一些函敎ͼ或调用其他一些函数?br /> MainWndProc(HWND hWnd, UINT message, W PARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_CREATE:
: : :
break;
case WM_PAINT:
: : :
break;
default:
return(DefWindowProc(hWnd,message,wParam,lParam));
}
return(NULL);
}
M遗漏的消息将被传输到一个默认的消息处理函数Q但是,case语句不能很好地适应C++和封装技术。在C++环境中,要求消息被一个专门处理该cd消息的类的成员函数处理。因此,MFC不采用case语句Q而采用更加复杂和回旋的方法。但它允许用U有cd理消息,而只需做下面三件事情:(x)
?从将要接收消息的CWndcd象派生类(对于命o(h)消息是CCmdTarget)?br /> ?在派生类中写一个处理消息的成员函数?br /> ?在类中定义一个查找表(叫做消息映像)Q该表具有成员函数的条目和它要处理的消息的标识符?br /> 然后QMFC依次调用下面的函敎ͼ指引输入消息到处理函数?br /> 1) AfxWndProc( )接收消息Q寻找消息所属的CWnd对象Q然后调用AfxCallWndProc( )?br /> 2) AfxCallWndProc( )存储消息(消息标识W和参数)供未来参考,然后调用WindowProc( )?br /> 3) WindowProc( ) 发送消息给OnWndMsg( ) Q然后,如果消息未被处理Q则发送给DefWindowproc( )?br /> 4) OnWndMsg( )要么为WM_COMMAND消息调用OnCommand( )Q要么ؓ(f)WM_NOTIFY消息调用OnNotify( )。Q何被遗漏的消息都是一个窗口消息。OnWndMsg( )搜烦(ch)cȝ消息映像Q以扑ֈ一个能处理MH口消息的处理函数。如果OnWndMsg( )不能扑ֈq样的处理函敎ͼ则把消息q回到WindowProc( )Q由它将消息发送给DefWindowProc( )?br /> 5) OnCommand()查看q是不是一个控仉知(lParam不是NULL)Q如果它是,OnCommand( )p囑ְ消息映射到制造通知的控Ӟ如果它不是一个控仉知Q或者控件拒l映的消息QOnCommand( )p用OnCmdMsg( )?br /> 6) OnNotify( )也试囑ְ消息映射到制造通知的控Ӟ如果映射不成功, OnNotify( )p用相同的OnCmdMsg( )函数?br /> 7) Ҏ(gu)接收消息的类QOnCmdMsg( )在一个称为命令传?Command Routing)的过E中潜在C递命令消息和控g通知。例如,如果拥有该窗口的cL一个框架类Q则命o(h)和通知消息也被传递到视图和文档类Qƈcd找一个消息处理函数?br />Z么要消息映像Q?br /> q毕竟是C++语言Qؓ(f)什么OnWndMsg( )不ؓ(f)每个H口消息调用一个预定义的虚拟函敎ͼ因ؓ(f)它太占CPU。若是那P当扫描一个消息映像以加速该q程ӞOnWndMsg( )可能?x)做出意想不到的事情Qƈ陷入汇编器。注意通过重蝲WindowProc( )、OnWndMsg( )、OnCommand( )、OnNotify( ) 或OnCmdMsg( )可以修改q一q程。重载OnWndMsg( )可以在窗口消息被排序之前插入该过E。重载OnCommand( )或OnNotify( )可以在消息被反射之前插入该过E?/p>