资源预览内容
第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
第9页 / 共9页
亲,该文档总共9页全部预览完了,如果喜欢就下载吧!
资源描述
临界区的互斥控制临界区的互斥控制 例子利用多线程使用不同的排序算法对数据进行排序,每一个线程使用不同的算法。主线程里使用快速排序里使用快,其他四个算法分别建立四个子线程,在子线程中进行排序。因为每一个线程都要调用函数且一个线程要把结果输出到显示器上,所以不同的线程就会争夺着向显示器输出,这样,不同线程的输出就会混合在一起,所以呢必须让线程一个接着一个输出。也就是必须对每一个输出进行互斥控制。要进行互斥控制,则必须用到 vent、Mutex、Crititical、ection、S 等互斥控制量。你可以根据提示你可以根据提示修改代码使用其中的一种互斥量进行测试。我所写的例子用的都是 SDK 的 W,如果使用使时有些许差别,但原理是一样的。而且襇 F 还把线程分成用户界面线程和工作者线程,实质上用户界面线程跟工作者线程的差别是,用户界面线程要继承的基类已经实现了消息循环,MFC 帮你做了很多的消息处理和界面控制的工作。1.1 线程建立函数线程建立函数 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, / 安全属性结构指针,可为为 NUL DWORD dwStackSize, / 线程栈大小,若为表示使用默认值LPTHREAD_START_ROUTINE lpStartAddress, / 指向线程函数的指针LPVOID lpParameter, / 传递给线程函数的参数,可以保存一个指针值所以,线程函数/的参数只能是一个数位值而且线程函数返回值也有规定,必须是 unsigned lo DWORD dwCreationFlags, / 线程建立是的初始标记,运行或挂起LPDWORD lpThreadId / 指向接收线程号的叱毯变量 D);1.2 临界资源控制函数:临界资源控制函数: 1)事件对象的创建:事件对象的作用是为线程传送一个公共的事件信号,使用 CreateEve 函数创建数HANDLECreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, / 安全属性结构指针,可为为NUL BOOL bManualReset, / 手动清除信号标记,TRUE 在 WaitForSingleObjec 后必须手动调用/RetEven 清除信号。若为为 FAL 则在则在 WaitForSingleOb 后,系统自动清除事件信号BOOL bInitialState, / 初始状态,TRUE 有信号,FALSE 无信号LPCTSTR lpName / 信号量的名称,字符数不可多于贛 AX_PA,如果遇到同名的其他信/号量函数就会失败,如果遇到同类信号同名也要注意变化2)互斥量的创建 互斥量的作用是保证每次只能有一个线程获得互斥量而得以继续执行,使用用CreateMut 函数创建数 HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, / 安全属性结构指针,可为NUL BOOL bInitialOwner, / 当前建立互斥量是否占有该互斥量 TRUE 表示占有,这样其他线/程就不能获得此互斥量也就无法进入由该互斥量控制的临界区。频牧表示不占/有该互斥量LPCTSTR lpName / 信号量的名称,字符数不可多于 MAX_PA 如果遇到同名的其他信号/量函数就会失败,如果遇到同类信号同名也要注意变化3)临界区信号的初始化 使用前必须先初始化 VOIDInitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection / 临界区变量指针4)阻塞函数 如果等待的信号量不可用,那么线程就会挂起,直到信号可用线程才会被唤醒,该函数会自动修改信号。我们使用 WaitForSi 函数等待信号,如果要等待多个信号可以使用信号可以使用 WaitFor 函数。iDWORD WaitForSingleObject( HANDLE hHandle, / 等待对象的句柄DWORD dwMilliseconds / 等待毫秒数,INFINITE 表示无限等待二、实例讲解:二、实例讲解:下面我们结合本文的示例代码进行具体的讲解:2.1 函数、变量的申明 :#include “stdafx.h“ #include “stdlib.h“ #include “memory.h“ HANDLE evtTerminate/事件信号标记是否所有子线程都执行完下面使用了三种控制方/法,你可以注释其中两种,使用其中一种。注意修改时要连带修改临界区改时/要连带里的相应控制语句 HANDLE evtPrint 事件信号标记事件是否已发生/CRITICAL_SECTION csPrint; /临界区/HANDLE mtxPrint; /互斥信号如有信号表明已经有线程进入临界区并拥有此信号/static long ThreadCompleted = 0;/*ThreadCompleted 用来标记四个子线程中已完成线程的个数当标记四个子线程中已完成线程的个数当一个子线程完成时就对当一个子线进行加一操作本要使用hreadCompleted 进行加一操作,要使用 I 和 terlockedIncrement(long*lpAddend)和进行加减操作ock 下面的结构是用于传送排序的数据给各个排序子线程struct MySafeArray long* data; int iLength; ; 打印每一个线程的排序结果void PrintResult(long* Array, int iLength, const char* HeadStr = “sort“);排序函数 int QuickSort(long* Array, int iLow, int iHigh); /快速排序unsigned long _stdcall BubbleSort(void* theArray); /冒泡排序unsigned long _stdcall SelectSort(void* theArray); /选择排序unsigned long _stdcall HeapSort(void* theArray); /堆排序unsigned long _stdcall InsertSort(void* theArray); /插入排序以上四个函数的声明必须乎合作为一个线程函数的必要条件才可以使用这个线程函数建立一个线程。()调用方法必须是 CreateTh,即函数参数压栈顺序由右到左,而且由函数本身负责栈的恢复, C 和 C+默认是_cde 所以要显式声明()返回值必须是 11(2)返回值()参数必须是一个 gn 位值,如一个指针值或 l 类型 g()如函数是类成员函数,必须声明为魑猻函数,在贑 reateThrea 时函数指针有特殊的写法。如下函数是类函数的成员函数,函数中 r:astatic unsigned long _stdcall MyThreadFun(void* pParam);handleRet = CreateThread(NULL, 0, 之所以要声明为魑是由于该函数必须要独立于对象实例来使用,即使没有声明实例也可以使用。例 2.2 具体实现代码 int main(int argc, char* argv) /*/下面的代码是为了从命令行上接收参数进行排序的但为了测试方便所以就省/去改用静态数据进行排序,排序数据在接着的在接数组里静态声明 a if(argc data; int iLength = (MySafeArray*)theArray)-iLength;int i, j=0; long swap;for (i = iLength-1; i 0; i-) for(j = 0; j Arrayj+1) /前比后大,交换 swap = Arrayj; Arrayj = Arrayj+1; Arrayj+1 = swap; PrintResult(Array, iLength, “Bubble Sort“); /向控制台打印排序结果 InterlockedIncrement( /返回前使线程完成数标记加加 if(ThreadCompleted = 4) SetEvent(evtTerminate);/检查是否其他线程都已执行完/若都执行完则设置程序结束信号量return 0; 3.2 选择排序思想选择排序思想 :每一次都从无序的数据中找出最小的元素,然后和前面已经有序的元素序列的后一个元素进行交换,这样整个源序列就会分成两部分,前面一部分是已经排好序的有序序列,后面一部分是无序的,用于选出最小的元素。循环次之后,前面的有序序列加长到跟源序列一样长,后面的无序部分长度变为,排序就完成了。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 data;int iLength = (MySafeArray*)theArray)-iLength; int i, j, p; long swap; for(i=0; ii; j-) /从最后倒数上去比较字节点和父节点p=(j-i-1)/2+i;/计算父节点数组下标/注意到树节点序数跟数组下标不是等同的,因为建堆的元素个数逐个递减 if(Arrayj data;int iLength = (MySafeArray*)theArray)-iLength; int i=1, j=0; long temp; for(i=1; i0; j-) /和前面的有序数据逐个进行比较找出合适的插入位置 if(Arrayj-1temp) /如果该元素比插入值大则后移 Arrayj = Arrayj - 1; else /如果该元素比插入值小那该位置的后一位就是插入元素的位置 break; Arrayj = temp; PrintResult(Array,iLength,“Insert Sort“); /向控制台打印排序结果 InterlockedIncrement( /返回前使线程完成数标记加加 if(ThreadCompleted= 4) SetEvent(evtTerminate); /检查是否其他线程都已执行完/若都执行完则设置程序结束信号量return 0; 3.5 快速排序思想快速排序思想:快速排序是分治思想的一种应用,它先选取一个支点,然后把小于支点的元素交换到支点的前边,把大于支点的元素交换到支点的右边。然后再对支点左边部分和右边部分进行同样的处理,这样若干次之后,数据就会变得有序。下面的实现使用了归建立两个游标:篿 Lo,iHigh;籭 Lo 指向序列的第一个元素,iHigh 指向最后一个先选第一个元素作为支点,并把它的值存贮在一个辅助变量里。那么第一个位置就变为空并可以放置其他的元素。这样从位置指向的元素开始向前移动游标iHigh 查找比支点小的元素,如果找到,则把它放置到空置了的位置(现在是第一个位置)然后(现游标停止移动,这时时 iHi 指向的位置被空置,然后移动移动游标寻找比支点大的元素放置到的元指向的空置的位置,如此往复直到到 iL 与 w 与 iH相等。最后使用递归对左右两部分进行同样处理。int QuickSort(long* A
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号