大连东软信息学院多核多线程实验一.docx

上传人:b****1 文档编号:15053570 上传时间:2023-06-30 格式:DOCX 页数:15 大小:36.40KB
下载 相关 举报
大连东软信息学院多核多线程实验一.docx_第1页
第1页 / 共15页
大连东软信息学院多核多线程实验一.docx_第2页
第2页 / 共15页
大连东软信息学院多核多线程实验一.docx_第3页
第3页 / 共15页
大连东软信息学院多核多线程实验一.docx_第4页
第4页 / 共15页
大连东软信息学院多核多线程实验一.docx_第5页
第5页 / 共15页
大连东软信息学院多核多线程实验一.docx_第6页
第6页 / 共15页
大连东软信息学院多核多线程实验一.docx_第7页
第7页 / 共15页
大连东软信息学院多核多线程实验一.docx_第8页
第8页 / 共15页
大连东软信息学院多核多线程实验一.docx_第9页
第9页 / 共15页
大连东软信息学院多核多线程实验一.docx_第10页
第10页 / 共15页
大连东软信息学院多核多线程实验一.docx_第11页
第11页 / 共15页
大连东软信息学院多核多线程实验一.docx_第12页
第12页 / 共15页
大连东软信息学院多核多线程实验一.docx_第13页
第13页 / 共15页
大连东软信息学院多核多线程实验一.docx_第14页
第14页 / 共15页
大连东软信息学院多核多线程实验一.docx_第15页
第15页 / 共15页
亲,该文档总共15页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

大连东软信息学院多核多线程实验一.docx

《大连东软信息学院多核多线程实验一.docx》由会员分享,可在线阅读,更多相关《大连东软信息学院多核多线程实验一.docx(15页珍藏版)》请在冰点文库上搜索。

大连东软信息学院多核多线程实验一.docx

大连东软信息学院多核多线程实验一

项目一:

Windows*Threads多线程编程

模块一:

基础模块

4编译执行,输出结果:

简答与思考:

1写出修改后的HelloThreads的代码。

//HelloThreads.cpp:

定¡§义°?

控?

制?

台¬¡§应®|用®?

程¨¬序¨°的Ì?

入¨?

口¨²点Ì?

¡ê

#include"stdafx.h"

#include

 

constintnumThreads=4;

DWORDWINAPIhelloFunc(LPVOIDarg)

{

intmyNum=*((int*)arg);

printf("HelloThread!

Thread%d\n",myNum);

return0;

}

intmain(intargc,_TCHAR*argv[])

{

HANDLEhThread[numThreads];

inttNum[numThreads];

for(inti=0;i

{tNum[i]=i;

hThread[i]=

CreateThread(NULL,0,helloFunc,&tNum[i],0,NULL);

}

WaitForMultipleObjects(numThreads,hThread,TRUE,INFINITE);

return0;

}

2项目总结。

在主线程中循环调用CreateThread()函数生成四个子线程分别去执行helloFunc()函数,并将线程的句柄保存在hThread数组中,CreateThread()的第四个参数给每个线程传递参数tNum获取该线程号,由helloFunc()的参数接收并指向内容。

由于主线程为每个子线程传递了不同的参数,所以通过子线程的执行结果可以判断出四个线程的执行顺序。

这个实验要我们初步认识了程序的并行化设计思想,为以后的并行化程序设计打下了基础。

 

3模块二:

临界区模块

3编译执行,记录结果:

Pi=

Thetimeofcalculationwas

seconds

6编译执行,记录结果:

Pi=

Thetimeofcalculationwas

seconds

7加速比:

1.,并行效率:

0.47625988

简答与思考:

1如何进行并行化的?

请写出并行化的思路与具体的代码。

答:

在程序中创建两个子线程,另主线程等待所有的子线程执行结束后退出。

代码:

#include"stdafx.h"

#include

#include

staticlongnum_steps=1000000000;

constintgNumThreads=2;

doublestep=0.0,pi=0.0,sum=0.0;

CRITICAL_SECTIONgCS;

DWORDWINAPIthreadFunction(LPVOIDpArg)

{

intmyNum=*((int*)pArg);

doublesum1=0.0,x;

for(inti=myNum;i

x=(i+0.5)*step;

sum1=sum1+4.0/(1.0+x*x);

}

EnterCriticalSection(&gCS);

sum+=sum1;

LeaveCriticalSection(&gCS);

return0;

}

intmain(intargc,_TCHAR*argv[])

{clock_tstart,stop;

start=clock();

HANDLEthreadHandles[gNumThreads];

inttNum[gNumThreads];

InitializeCriticalSection(&gCS);

step=1.0/(double)num_steps;

for(inti=0;i

{

tNum[i]=i;

threadHandles[i]=CreateThread(NULL,0,threadFunction,&tNum[i],0,NULL);

}

WaitForMultipleObjects(gNumThreads,threadHandles,TRUE,INFINITE);

DeleteCriticalSection(&gCS);

pi=step*sum;

stop=clock();

printf("Pi=%12.12f\n",pi);

printf("Thetimeofcalculationwas%fseconds\n",((double)(stop-start)/1000.0));

return0;

}

4在本模块中,哪些变量是需要保护的?

为什么?

采取什么方法实现的?

答:

1)临界资源是需要保护的,也就是sum变量,这里将对sum的更新操作定义为临界区操作

2)在多线程程序中,对于在子线程执行的代码中出现的被更新的全局变量要多加考虑,因为具有这种特点的变量,很有可能会引起数据冲突,如果处理不好会使程序产生功能性错误

3)EnterCriticalSection(&gCS);

sum+=sum1;

LeaveCriticalSection(&gCS);

在创建子线程前调用InitializeCriticalSection(&gCS);函数对临界区进行初始化,然后调用EnterCriticalSection(&gCS);函数获取临界区的访问权,访问结束后调用LeaveCriticalSection(&gCS);函数释放临界区的访问权,在退出程序前需要调用DeleteCriticalSection(&gCS);函数注销临界区,释放临界区资源。

3是否可以对该并行化方案进行进一步的优化?

如何优化?

答:

可以。

更改创建线程的数目,分别计算出加速比和效率,比较结果进行取舍。

4项目总结。

在使用临界区时,可以根据需要,在程序中定义多个临界区对象,用来对不同的代码段进行互斥访问,这样做的好处是可以增加程序的并行度。

总之,在设计多线程程序时,应该为每一个需要互斥的共享资源定义一个临界区变量。

 

模块三:

事件模块

3编译执行,记录结果:

Resultis

Thetimeofcalculationwas

seconds

4阅读代码,回答下面问题。

(1)主线程共创建5个子线程。

(2)各子线程调用的函数各是什么?

DWORD WINAPI threadProc(LPVOID par){}

DWORD WINAPI masterThreadProc(LPVOID par){}

(3)主线程等待4个子线程的执行结束。

6改进后的,编译执行,记录结果:

Resultis

Thetimeofcalculationwas

seconds

简答与思考:

1在WINAPIthreadProc(LPVOIDpar){}函数中为什么用临界区互斥了线程对threadCount的访问?

为什么对于全局数据变量sums的访问没有互斥?

WINAPIthreadProc(LPVOIDpar){}函数使用多线程执行的threadCount是记录线程个数的,而它对于masterThreadProc()函数要等待所有线程(threadCount)做完对sums的计算,然后再进行最后的四个线程结构的累加,它是这两个函数的共享变量,对于它的操作必须原子化,以保证每次只有一个线程对其自增,所以使用临界区互斥了每个线程对threadCount的访问,避免了数据冲突的发生。

而对于数据变量sums每一个数组元素下标值是每个子线程获得的参数,该参数标记了各个子线程,sums数组分别对应每一个线程,从而使每个子线程操作的变量分别保存在对应的数组元素中,四个线程互不影响,所以并不需要互斥。

2简述源代码中存在的问题,详述提出的改进方案及相关代码。

源代码中为使“master”子线程等待其余四个子线程执行完毕,使用空循环保持“master”子线程的“等待”状态,这显然不是好的方法;改进方案中使用了事件,事件用于线程间的执行顺序以保证对共享资源操作的完整性,本程序中,“master”子线程是另外创建的,它需要另外四个子线程的执行结果,所以需要等待以保证获得它们的结果后再进行操作,使用事件机制,程序中定义四个未激发的人工重置事件,“master”子进程在执行时以wait方式等待事件被激发,由于其余四个子线程在完成任务后将事件从未激发设置为激发态,从而使“master”子线程继续执行余下操作

#include"stdafx.h"

#include

#include

#include

#include

 

#defineNUMTHREADS4

#defineSERIES_MEMBER_COUNT100000

HANDLE*threadHandles,masterThreadHandle,*eventHandles;

CRITICAL_SECTIONcountCS;

double*sums;

doublex=1.0,res=0.0;

intthreadCount=0;

doublegetMember(intn,doublex)

{

doublenumerator=1;

for(inti=0;i

numerator=numerator*x;

if(n%2==0)

return(-numerator/n);

else

returnnumerator/n;

}

DWORDWINAPIthreadProc(LPVOIDpar)

{

intthreadIndex=*((int*)par);

sums[threadIndex]=0;

for(inti=threadIndex;i

sums[threadIndex]+=getMember(i+1,x);

SetEvent(eventHandles[threadIndex]);

//SignalMasterthreadthatonemoreprocessingthreadisdone

//EnterCriticalSection(&countCS);

//threadCount++;

//LeaveCriticalSection(&countCS);

deletepar;

return0;

}

DWORDWINAPImasterThreadProc(LPVOIDpar)

{

for(inti=0;i

//while(threadCount!

=NUMTHREADS){}//busywaituntilallthreadsaredonewithcomputationofpartialsums

WaitForMultipleObjects(NUMTHREADS,eventHandles,TRUE,INFINITE);

res=0;

for(inti=0;i

res+=sums[i];

return0;

}

intmain()

{

clock_tstart,stop;

threadHandles=newHANDLE[NUMTHREADS+1];

eventHandles=newHANDLE[NUMTHREADS+1];

//InitializeCriticalSection(&countCS);

sums=newdouble[NUMTHREADS];

start=clock();

for(inti=0;i

{

int*threadIdPtr=newint;

*threadIdPtr=i;

threadHandles[i]=CreateThread(NULL,0,threadProc,threadIdPtr,CREATE_SUSPENDED,NULL);

eventHandles[i]=CreateEvent(NULL,TRUE,FALSE,NULL);

}

threadHandles[NUMTHREADS]=CreateThread(NULL,0,masterThreadProc,NULL,0,NULL);

printf("Countofln(1+x)Mercator'sseriesmembersis%d\n",SERIES_MEMBER_COUNT);

printf("Argumentvalueofxis%f\n",(double)x);

WaitForMultipleObjects(NUMTHREADS+1,threadHandles,TRUE,INFINITE);

stop=clock();

for(inti=0;i

deletethreadHandles;

deleteeventHandles;

//DeleteCriticalSection(&countCS);

deletesums;

printf("Resultis%10.8f\n",res);

printf("Byfunctioncallln(1+%f)=%10.8f\n",x,log(1+x));

printf("Thetimeofcalculationwas%fseconds\n",((double)(stop-start)/1000.0));

printf("Pressanykey...");

getch();

return0;

}

3是否可以对该并行化方案进行进一步的优化?

如何优化?

线程在被创建时就执行,不再去唤醒,“master”只需等待事件被激发,效率就会有所提高。

4项目总结。

 

模块四:

信号量模块

3这是串行代码实现,编译执行,记录结果。

6编译执行并行版本,多次运行,记录结果:

第1次执行结果:

第2次执行结果:

第3次执行结果:

10修正后项目的输出结果为:

简答与思考:

1SemaphoreS项目与SemaphoreT项目执行结果不一致的原因是什么?

在多线程中fd和TotalWords,TotalEventWords,TotalOddWords属于共享变量,在并发执行的过程中会造成数据冲突。

fd对于每个线程是互斥的,是因为在文件指针往下一行改变时,不准许其它线程对该操作有影响,不然就会造成该问题的计数结果不正确的现象;而TotalWords,TotalEventWords,TotalOddWords这几个变量,是计算总的字符串个数,含有偶数数量字符的字符串的个数及含有奇数个字符的字符串的个数,毋庸置疑,它们也是共享资源,在对他们进行累加时,要注意数据冲突。

2如何修改SemaphoreT项目源代码?

写出修改思路和关键代码。

可以采用临界区的方法去做,也可以采用信号量。

信号量也是一种内核对象,它可以对当前的资源计数,这是与临界区最大的不同,当资源数量大于0时,等待该信号量的线程就可以获得该资源得以继续执行。

改进方案:

在代码中应用两个信号量hSem1,hSem2,hSem1用于线程对文件指针fd的互斥,hSem2用于对全局变量TotalWords,TotalEventWords,TotalOddWords的互斥,对于每一个子线程,由于我们的思路是按行计算,然后累加的,对于偶数数量字符的字符串的个数以及含有奇数数量字符串的个数计算,要注意采取巧妙点的处理办法。

#include"stdafx.h"

#include

#include

#include

FILE*fd;

intTotalEvenWords=0,TotalOddWords=0,TotalWords=0;

HANDLEhSem1,hSem2;

constintNUMTHREADS=4;

 

intGetNextLine(FILE*f,char*Line)

{

if(fgets(Line,132,f)==NULL)if(feof(f))returnEOF;elsereturn1;

}

intGetWordAndLetterCount(char*Line)

{

intWord_Count=0,OddWords=0,EvenWords=0,Letter_Count=0;

for(inti=0;i<132;i++)

{

if((Line[i]!

='')&&(Line[i]!

=0)&&(Line[i]!

='\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(Line[i]==0)break;

}

}

return(Word_Count*10000+OddWords*100+EvenWords);

}

DWORDWINAPICountWords(LPVOIDarg)

{

BOOLbDone=FALSE;

charinLine[132];

intlCount=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;

TotalOddWords+=(lCount%10000)/100;

TotalEvenWords+=lCount%100;

ReleaseSemaphore(hSem2,1,NULL);//出?

}

}

return0;

}

intmain()

{

HANDLEhThread[NUMTHREADS];

hSem1=CreateSemaphore(NULL,1,1,NULL);

hSem2=CreateSemaphore(NULL,1,1,NULL);

fd=fopen("In","r");//Openread

for(inti=0;i

hThread[i]=CreateThread(NULL,0,CountWords,NULL,0,NULL);

WaitForMultipleObjects(NUMTHREADS,hThread,TRUE,INFINITE);

fclose(fd);

printf("TotalWords=%8d\n\n",TotalWords);

printf("TotalEvenWords=%7d\nTotalOddWords=%7d\n",TotalEvenWords,TotalOddWords);

getchar();

return0;

}

 

3是否还有更优的修改方案?

是如何修改的?

 

4项目总结。

信号量这种内核对象,常被用于对于有限数据空间是访问控制、对一段代码的线程访问数量的控制、对有限资源的访问控制这几个方面。

信号量这种给资源计数的思想在很多方面都会用到,但是使用起来也要谨慎,用不好说不定会给程序带来潜在的死锁隐患。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 法律文书

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2