1、大连东软信息学院多核多线程实验一项目一 :Windows*Threads多线程编程模块一:基础模块4 编译执行, 输出结果: 简答与思考:1 写出修改后的HelloThreads的代码。/ HelloThreads.cpp : 定义?控?制?台应|用?程序的?入?口点?。#include stdafx.h#include const int numThreads = 4;DWORD WINAPI helloFunc(LPVOID arg) int myNum=*(int*)arg); printf(Hello Thread!Thread %d n,myNum); return 0; int m
2、ain(int argc,_TCHAR* argv) HANDLE hThreadnumThreads; int tNumnumThreads; for (int i = 0; i numThreads; i+) tNumi=i; hThreadi = CreateThread(NULL, 0, helloFunc, &tNumi, 0, NULL ); WaitForMultipleObjects(numThreads, hThread, TRUE, INFINITE); return 0; 2 项目总结。在主线程中循环调用CreateThread()函数生成四个子线程分别去执行helloF
3、unc()函数,并将线程的句柄保存在hThread数组中,CreateThread()的第四个参数给每个线程传递参数tNum获取该线程号,由helloFunc()的参数接收并指向内容。由于主线程为每个子线程传递了不同的参数,所以通过子线程的执行结果可以判断出四个线程的执行顺序。这个实验要我们初步认识了程序的并行化设计思想,为以后的并行化程序设计打下了基础。3 模块二:临界区模块3 编译执行,记录结果:Pi = The time of calculation was seconds6 编译执行,记录结果:Pi = The time of calculation was seconds7 加速比:
4、1. , 并行效率: 0.47625988 简答与思考:1 如何进行并行化的?请写出并行化的思路与具体的代码。 答:在程序中创建两个子线程,另主线程等待所有的子线程执行结束后退出。代码:#include stdafx.h#include #include static long num_steps=1000000000; const int gNumThreads=2;double step=0.0,pi=0.0,sum=0.0;CRITICAL_SECTION gCS;DWORD WINAPI threadFunction(LPVOID pArg) int myNum=*(int *)pAr
5、g); double sum1=0.0,x; for (int i=myNum; i num_steps; i+=gNumThreads) x = (i+0.5)*step; sum1 = sum1 + 4.0/(1.0 + x*x); EnterCriticalSection(&gCS); sum+=sum1; LeaveCriticalSection(&gCS); return 0;int main(int argc,_TCHAR* argv) clock_t start, stop; start = clock(); HANDLE threadHandlesgNumThreads; in
6、t tNumgNumThreads; InitializeCriticalSection(&gCS); step = 1.0/(double) num_steps; for(int i=0;igNumThreads;+i) tNumi=i; threadHandlesi=CreateThread(NULL,0,threadFunction,&tNumi,0,NULL); WaitForMultipleObjects(gNumThreads,threadHandles,TRUE,INFINITE); DeleteCriticalSection(&gCS); pi = step * sum; st
7、op = clock(); printf(Pi = %12.12fn,pi); printf(The time of calculation was %f secondsn,(double)(stop - start)/1000.0); return 0;4 在本模块中,哪些变量是需要保护的?为什么?采取什么方法实现的?答:1)临界资源是需要保护的,也就是sum变量,这里将对sum的更新操作定义为临界区操作2)在多线程程序中,对于在子线程执行的代码中出现的被更新的全局变量要多加考虑,因为具有这种特点的变量,很有可能会引起数据冲突,如果处理不好会使程序产生功能性错误3)EnterCritical
8、Section(&gCS); sum+=sum1;LeaveCriticalSection(&gCS); 在创建子线程前调用InitializeCriticalSection(&gCS);函数对临界区进行初始化,然后调用EnterCriticalSection(&gCS);函数获取临界区的访问权,访问结束后调用LeaveCriticalSection(&gCS);函数释放临界区的访问权,在退出程序前需要调用DeleteCriticalSection(&gCS);函数注销临界区,释放临界区资源。3是否可以对该并行化方案进行进一步的优化?如何优化? 答:可以。更改创建线程的数目,分别计算出加速比和
9、效率,比较结果进行取舍。4 项目总结。在使用临界区时,可以根据需要,在程序中定义多个临界区对象,用来对不同的代码段进行互斥访问,这样做的好处是可以增加程序的并行度。总之,在设计多线程程序时,应该为每一个需要互斥的共享资源定义一个临界区变量。模块三:事件模块3 编译执行,记录结果:Result is The time of calculation was seconds4 阅读代码,回答下面问题。(1)主线程共创建 5 个子线程。(2)各子线程调用的函数各是什么? DWORDWINAPIthreadProc(LPVOIDpar) DWORDWINAPImasterThreadProc(LPVOI
10、Dpar) (3)主线程等待 4 个子线程的执行结束。6 改进后的,编译执行,记录结果:Result is The time of calculation was seconds简答与思考:1 在WINAPI threadProc(LPVOID par)函数中为什么用临界区互斥了线程对threadCount的访问?为什么对于全局数据变量sums的访问没有互斥? WINAPI threadProc(LPVOID par)函数使用多线程执行的threadCount是记录线程个数的,而它对于masterThreadProc()函数要等待所有线程(threadCount)做完对sums的计算,然后再进
11、行最后的四个线程结构的累加,它是这两个函数的共享变量,对于它的操作必须原子化,以保证每次只有一个线程对其自增,所以使用临界区互斥了每个线程对threadCount的访问,避免了数据冲突的发生。 而对于数据变量sums每一个数组元素下标值是每个子线程获得的参数,该参数标记了各个子线程,sums数组分别对应每一个线程,从而使每个子线程操作的变量分别保存在对应的数组元素中,四个线程互不影响,所以并不需要互斥。2 简述源代码中存在的问题,详述提出的改进方案及相关代码。 源代码中为使“master”子线程等待其余四个子线程执行完毕,使用空循环保持“master”子线程的“等待”状态,这显然不是好的方法;
12、改进方案中使用了事件,事件用于线程间的执行顺序以保证对共享资源操作的完整性,本程序中,“master”子线程是另外创建的,它需要另外四个子线程的执行结果,所以需要等待以保证获得它们的结果后再进行操作,使用事件机制,程序中定义四个未激发的人工重置事件,“master”子进程在执行时以wait方式等待事件被激发,由于其余四个子线程在完成任务后将事件从未激发设置为激发态,从而使“master”子线程继续执行余下操作#include stdafx.h#include #include #include #include#define NUMTHREADS 4#define SERIES_MEMBER_
13、COUNT 100000HANDLE *threadHandles, masterThreadHandle,*eventHandles;CRITICAL_SECTION countCS;double *sums;double x = 1.0, res = 0.0;int threadCount = 0;double getMember(int n, double x) double numerator = 1; for( int i=0; in; i+ ) numerator = numerator*x; if ( n % 2 = 0 ) return ( - numerator / n );
14、 else return numerator/n;DWORD WINAPI threadProc(LPVOID par) int threadIndex = *(int *)par); sumsthreadIndex = 0; for(int i=threadIndex; iSERIES_MEMBER_COUNT;i+=NUMTHREADS) sumsthreadIndex += getMember(i+1, x); SetEvent(eventHandlesthreadIndex); /Signal Master thread that one more processing thread
15、is done /EnterCriticalSection(&countCS); /threadCount+; /LeaveCriticalSection(&countCS); delete par; return 0;DWORD WINAPI masterThreadProc(LPVOID par) for( int i=0; iNUMTHREADS; i+ ) ResumeThread(threadHandlesi); / Start computing threads /while (threadCount != NUMTHREADS) / busy wait until all thr
16、eads are done with computation of partial sums WaitForMultipleObjects(NUMTHREADS,eventHandles,TRUE,INFINITE); res = 0; for(int i=0; iNUMTHREADS; i+) res += sumsi; return 0;int main() clock_t start,stop; threadHandles = new HANDLENUMTHREADS + 1; eventHandles = new HANDLENUMTHREADS +1 ; /InitializeCri
17、ticalSection(&countCS); sums = new doubleNUMTHREADS; start=clock(); for(int i=0; iNUMTHREADS;i+) int * threadIdPtr = new int; *threadIdPtr = i; threadHandlesi = CreateThread(NULL, 0, threadProc, threadIdPtr, CREATE_SUSPENDED, NULL); eventHandlesi=CreateEvent(NULL,TRUE,FALSE,NULL); threadHandlesNUMTH
18、READS = CreateThread(NULL, 0, masterThreadProc, NULL, 0, NULL); printf(Count of ln(1 + x) Mercators series members is %dn,SERIES_MEMBER_COUNT); printf(Argument value of x is %fn, (double)x); WaitForMultipleObjects(NUMTHREADS+1,threadHandles,TRUE,INFINITE); stop=clock(); for(int i=0; iNUMTHREADS+1; i
19、+ ) CloseHandle(threadHandlesi); delete threadHandles; delete eventHandles; /DeleteCriticalSection(&countCS); delete sums; printf(Result is %10.8fn, res); printf(By function call ln(1 + %f) = %10.8fn,x, log(1+x); printf(The time of calculation was %f secondsn,(double)(stop-start)/1000.0); printf(Pre
20、ss any key . ); getch(); return 0;3是否可以对该并行化方案进行进一步的优化?如何优化? 线程在被创建时就执行,不再去唤醒,“master”只需等待事件被激发,效率就会有所提高。4 项目总结。 模块四:信号量模块3 这是串行代码实现,编译执行,记录结果。6 编译执行并行版本,多次运行,记录结果: 第1次执行结果:第2次执行结果:第3次执行结果:10 修正后项目的输出结果为:简答与思考:1 SemaphoreS项目与SemaphoreT项目执行结果不一致的原因是什么? 在多线程中fd和TotalWords,TotalEventWords,TotalOddWords
21、属于共享变量,在并发执行的过程中会造成数据冲突。fd对于每个线程是互斥的,是因为在文件指针往下一行改变时,不准许其它线程对该操作有影响,不然就会造成该问题的计数结果不正确的现象;而TotalWords,TotalEventWords,TotalOddWords这几个变量,是计算总的字符串个数,含有偶数数量字符的字符串的个数及含有奇数个字符的字符串的个数,毋庸置疑,它们也是共享资源,在对他们进行累加时,要注意数据冲突。2 如何修改SemaphoreT项目源代码?写出修改思路和关键代码。可以采用临界区的方法去做,也可以采用信号量。信号量也是一种内核对象,它可以对当前的资源计数,这是与临界区最大的不
22、同,当资源数量大于0时,等待该信号量的线程就可以获得该资源得以继续执行。改进方案:在代码中应用两个信号量hSem1,hSem2,hSem1用于线程对文件指针fd的互斥,hSem2用于对全局变量TotalWords,TotalEventWords,TotalOddWords的互斥,对于每一个子线程,由于我们的思路是按行计算,然后累加的,对于偶数数量字符的字符串的个数以及含有奇数数量字符串的个数计算,要注意采取巧妙点的处理办法。#include stdafx.h#include #include #include FILE *fd; int TotalEvenWords = 0, TotalOdd
23、Words = 0, TotalWords = 0;HANDLE hSem1,hSem2;const int NUMTHREADS = 4;int GetNextLine(FILE *f, char *Line) if (fgets(Line, 132, f)=NULL) if (feof(f)return EOF; else return 1;int GetWordAndLetterCount(char *Line) int Word_Count = 0,OddWords=0,EvenWords=0, Letter_Count = 0; for (int i=0;i132;i+) if (L
24、inei!= )&(Linei!=0)&(Linei!=n) Letter_Count+; else if(Letter_Count!=0) if (Letter_Count % 2) OddWords+; Word_Count+; Letter_Count = 0; else EvenWords+; Word_Count+; Letter_Count = 0; if (Linei=0) break; return (Word_Count*10000+OddWords*100+EvenWords); DWORD WINAPI CountWords(LPVOID arg) BOOL bDone
25、= FALSE ; char inLine132; int lCount=0; while (!bDone) WaitForSingleObject(hSem1,INFINITE);/进? bDone = (GetNextLine(fd, inLine) = EOF); ReleaseSemaphore(hSem1,1,NULL);/出? if (!bDone) lCount=GetWordAndLetterCount(inLine); WaitForSingleObject(hSem2,INFINITE); /进? TotalWords+=lCount/10000; TotalOddWord
26、s+=(lCount%10000)/100; TotalEvenWords += lCount%100; ReleaseSemaphore(hSem2,1,NULL); /出? return 0;int main() HANDLE hThreadNUMTHREADS; hSem1=CreateSemaphore(NULL,1,1,NULL); hSem2=CreateSemaphore(NULL,1,1,NULL); fd = fopen(In, r); / Open read for (int i = 0; i NUMTHREADS; i+) hThreadi = CreateThread(
27、NULL,0,CountWords,NULL,0,NULL); WaitForMultipleObjects(NUMTHREADS, hThread, TRUE, INFINITE); fclose(fd); printf(Total Words = %8dnn, TotalWords); printf(Total Even Words = %7dnTotal Odd Words = %7dn, TotalEvenWords, TotalOddWords); getchar(); return 0; 3 是否还有更优的修改方案?是如何修改的?无4 项目总结。信号量这种内核对象,常被用于对于有限数据空间是访问控制、对一段代码的线程访问数量的控制、对有限资源的访问控制这几个方面。信号量这种给资源计数的思想在很多方面都会用到,但是使用起来也要谨慎,用不好说不定会给程序带来潜在的死锁隐患。
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2