多核实验报告_第1页
多核实验报告_第2页
多核实验报告_第3页
多核实验报告_第4页
多核实验报告_第5页
已阅读5页,还剩26页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、实 验 报 告课程名称: 多核多线程技术 院 (系): 信息与控制工程学院 专业班级: 计算机科学与技术 姓 名: 学 号: 指导教师: 2012年11月 21日实验一 Windows API多线程编程本实验分为四个模块;分别为基础练习,临界区实验,事件实验,信号量实验。通过本次实验逐步熟悉和掌握Win32 API多线程编程的语法结构、基本思路和方法。理解API之间的调用关系,参数的含义。一、实验目的1、掌握MS Visual Studio 2010编写编译Win32 API多线程程序的方法;2、掌握Win 32 API编写多线程程序的语法;3、掌握Win 32 API编写多线程程序的思路;4

2、、掌握Win 32多线程API的应用;5、能解决简单的数据竞争。二、实验环境1、Windows XP 系统2、Microsoft Visual Studio 2010三、相关知识1、内核对象的概念:由操作系统内核分配的,只能由内核访问的一个内存块,用来供系统和应用程序使用和管理各种系统资源。2、Windows多线程API的基本管理线程的创建: HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID

3、 lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); 线程的终止: VOID ExitThread(DWORD dwExitCode) BOOL CloseHandle (HANDLE hObject) 线程的挂起与恢复: DWORD SuspendThread(HANDLE hThread) DWORD ResumeThread(HANDLE hThread) 线程间的等待: DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds); DWORD WaitFo

4、rMultipleObjects ( DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds); 3、线程间的同步 与临界区相关的API: void WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); void WINAPI EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); void WINAPI LeaveCriticalSection(L

5、PCRITICAL_SECTION lpCriticalSection); void WINAPI DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 与事件相关的API: HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName); DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMillisecond

6、s); DWORD WaitForMultipleObjects ( DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds); BOOL SetEvent(HANDLE event ); BOOL ResetEvent(HANDLE event ); 与信号量相关的API: HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpEventAttributes, LONG lSemInitial, LONG lSemMax, LPCSTR lpSemName)

7、; DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds); DWORD BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG cReleaseCount, LPLONG lpPreviousCount);四、实验原理线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件、信号标识及动态分配的内存等。一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度程序控制,调度程序决定哪个线程可执行以及什么时候执行线程。线程有优先

8、级别,优先权较低的线程必须等到优先权较高的线程执行完后再执行。在多核的机器上,调度程序可将多个线程放到不同的处理器核上去运行,这样可使处理器任务平衡,并提高系统的运行效率。Windows是一种多任务的操作系统,在Windows的一个进程内包含一个或多个线程。32位Windows环境下的Win32 API提供了多线程应用程序开发所需要的接口函数,而利用VC中提供的标准C库也可以开发多线程应用程序,相应的MFC类库封装了多线程编程的类,用户在开发时可根据应用程序的需要和特点选择相应的工具。多线程编程在Win32方式下和MFC类库支持下的原理是一致的,进程的主线程在任何需要的时候都可以创建新的线程。

9、当线程执行完后,自动终止线程;当进程结束后,所有的线程都终止。所有活动的线程共享进程的资源,因此,在编程时需要考虑在多个线程访问同一资源时产生冲突的问题。当一个线程正在访问某进程对象,而另一个线程要改变该对象,就可能会产生错误的结果,编程时要解决这个冲突。五、实验内容模块一:基础模块实验步骤: 1、 用Microsoft Visual Studio 2005创建控制台项目HelloThreads 。 2、 创建HelloThreads.cpp文件,内容如下: #include stdafx.h #include #include const int numThreads = 4; DWORD

10、WINAPI helloFunc(LPVOID pArg) printf(Hello Thread n); return 0; int _tmain(int argc, _TCHAR* argv) HANDLE hThreadnumThreads; int tNum10; for (int i = 0; i HelloThreads属性菜单,按图1-1图1-4配置项目属性。图1-1. 项目属性设置 C/C+ Folder Debug Options图1-2 项目属性设置 C/C+ Folder Optimization Options4、编译执行,输出结果如下:5、修改代码,使之输出结果可以表

11、示出各线程的输出顺序。修改代码如下:#include stdafx.h#include const int numThreads = 4;DWORD WINAPI helloFunc(LPVOID pArg) int j = *(int *)pArg; printf(Hello Thread %dn,j); return 0; int _tmain(int argc, _TCHAR* argv)HANDLE hThreadnumThreads; int tNum4;for (int i = 0; i numThreads; i+) tNumi=i; hThreadi = CreateThrea

12、d(NULL, 0, helloFunc, &tNumi, 0, NULL );WaitForMultipleObjects(numThreads, hThread, TRUE, INFINITE);return 0; 运行结果如下:模块二:临界区实验本模块将以数值积分的方法计算Pi的值,采用Win32 API来实现程序的并行化。实验步骤: 1、 用Microsoft Visual Studio 2005创建控制台项目WinPi 。2 、创建WinPi.cpp,内容如下:#include #include #include static long num_steps=;double step,

13、pi;int main()clock_t start,stop;start=clock();int i;double x,sum=0.0;step=1.0/(double) num_steps;for (i=0;inum_steps;i+)x=(i+0.5)*step;sum=sum+4.0/(1.0+x*x);pi=step*sum;stop=clock();printf(pi=%12.9fn,pi);printf(the time of was %f sn,(double)(stop-start)/1000.0);3、 编译执行, Pi的值为: 3. The time of calcula

14、tionwas 11. seconds4 、将WinPi.cpp程序修改为Windows Threads并行程序。(1)分析代码中的并行域是: (2)定义线程执行的函数。函数原型为DWORD WINAPI 函数名(LPVOID p); (3)提取并行域代码,作为(2)中定义的函数的函数体(4)生成多个线程调用(3)中的函数(5)解决线程间的同步和互斥。5 、采用临界区的方法进行必要的互斥。6 、编译执行,Pi的值为: 3. The time of calculationwas 6. seconds模块三:事件实验本实验利用麦凯特尔对数级数估算ln(1 + x), -1 x = 1的值。线程被创

15、建时是挂起的状态。这些线程被一个master线程唤醒。线程将分别执行自己的任务,master线程将等待所有的线程执行完毕后,汇总每一个线程的执行结果。实验步骤: 1、 用Microsoft Visual Studio 2005创建控制台项目ThreadEvent 。2、 创建ThreadEvent.cpp,内容如下: #include#include#include#include#include#define NUMTHREADS 4#define SERIES_MEMBER_COUNT HANDLE *threadHandles,masterThreadHandle;CRITICAL_SE

16、CTION 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);return numerator/n;DWORD WINAPI threadProc(LPVOID par)int threadIndex=*(int *)par);sumsthreadIndex=0;for(int i=

17、threadIndex;iSERIES_MEMBER_COUNT;i+=NUMTHREADS)sumsthreadIndex+=getMember(i+1,x);EnterCriticalSection(&countCS);threadCount+;LeaveCriticalSection(&countCS);delete par;return 0;DWORD WINAPI masterthreadProc(LPVOID par)for(int i=0;iNUMTHREADS;i+) ResumeThread(threadHandlesi);while(threadCount!=NUMTHRE

18、ADS)res=0;for(int i=0;iNUMTHREADS;i+)res+=sumsi;return 0;int main(int argc,CHAR *argv) clock_t start,stop;threadHandles=new HANDLENUMTHREADS+1;InitializeCriticalSection(&countCS);sums=new doubleNUMTHREADS;start=clock();for(int i=0;iNUMTHREADS;i+) int * threadIdPtr=new int;* threadIdPtr=i;threadHandl

19、esi=CreateThread(NULL,0,threadProc,threadIdPtr,CREATE_SUSPENDED,NULL);threadHandlesNUMTHREADS=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+

20、1,threadHandles,TRUE,INFINITE);stop=clock();for(int i=0;iNUMTHREADS+1;i+) CloseHandle(threadHandlesi);delete threadHandles;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,(dou

21、ble)(stop-start)/1000.0);printf(Press any key .);getch();return 0;3、编译运行,运行结果如下:Result is 0. ; The time of calculation was 19. seconds 。 模块四:信号量实验本实验对一个串行程序并行化,以信号量的方式解决数据冲突。代码分析了指定文件中字符串的个数并分别对含有偶数个字符的字符串以及含有奇数个字符的字符串的个数。实验步骤如下:1、 用MS Visual Studio 2010创建控制台项目SemaphoreS。2、 创建SemaphoreS.cpp,内容如下:#in

22、clude#includeFILE*fd;int TotalEvenWords=0,TotalOddWords=0,TotalWords=0;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 WordCount=0,LetterCount=0;for(int i=0;i132;i+)if(Linei!= )&(Linei!=0)&(Linei!=n) LetterCo

23、unt+;elseif(LetterCount!=0)if(LetterCount%2)TotalOddWords+;WordCount+;LetterCount=0;elseTotalEvenWords+;WordCount+;LetterCount=0;if(Linei=0)break;return (WordCount);DWORD WINAPI CountWords()BOOL bDone=FALSE;char inLine132;while(!bDone)bDone=(GetNextLine(fd,inLine)=EOF);if(!bDone)TotalWords+=GetWordA

24、ndLetterCount(inLine);return 0;int main()fd=fopen(InFiles.txt,r);CountWords();fclose(fd);printf(Total Words=%8dnn,TotalWords);printf(Total Even Words=%7dnTotal Odd Words=%7dn,TotalEvenWords,TotalOddWords);return 0;3、 确定串行版本项目为启动项,编译执行。 Total Words: _Total Even Words: _ Total Odd Words: _ 4、 确定并行版本项目

25、为启动项,编译运行,运行结果如下:Total Words: _Total Even Words: _ Total Odd Words: _ 5、 解决数据冲突进行修正,修正后项目的输出结果为:Total Words: Total Even Words: Total Odd Words: 六、实验总结此次试验我基本掌握了在Windows多线程编程基础中利用API函数进行多线程编程的基本步骤:1、 使用CreateThread创建线程;2、 SetThreadPriority设置线程优先权;3、 ExitThread在线程的执行过程中终止该线程;4、 TerminateThread线程的外面终止线

26、程。理解了线程的基本原理及使用的意义,为以后的多线程编程打下良好的基础。实 验二 OpenMP多线程编程实验本实验分为三个模块;分别为基础练习,数值积分计算Pi值,Monte Carlo计算Pi值。通过本次实验逐步熟悉和掌握OpenMP多线程编程的语法结构、基本思路和方法。一、实验目的1. 掌握Microsoft Visual Studio 编写编译OpenMP程序的方法。2. 掌握OpenMP编写多线程程序的语法。3. 掌握OpenMP编写多线程程序的思路。4. 能解决简单的数据竞争。 5. 了解数值积分计算和Monte Carlo计算二、实验环境1、Windows XP 系统2、Micro

27、soft Visual Studio 2010三、相关知识 OpenMP编程模型提供平台无关的编译指导,运行时库函数及环境变量,来显示的指导编译器对程序进行并行化。 线程的创建: #pragma omp parallel clauseclause . /Code to run in Parallel goes here 针对for循环的并行化: #pragma omp parallel for clauseclause for( xxx ; yyy ; zzz) / Loop body 可用于解决数据竞争的编译指导子句和API。使变量私有化的子句:private,lastprivate,fir

28、stprivate 。使变量共享的子句:shared。用于规约的子句:reduction。用于解决线程同步的方法有互斥锁和事件同步机制两类。其中互斥锁包括:critical,atomic,由OpenMP库函数提供的同步API;事件同步机制包括:隐式同步栅障,取消栅障的nowait子句,显式同步栅障的barrier子句以及sections,master,single等子句。 用于线程调度优化的编译指导:#pragma omp for schedule(kind , chunksize),其中kind表示采用的调度类型,可选值包括:static、dynamic、guided和runtime四种子句

29、。 OpenMP的常用库函数: int omp_get_num_procs(void) int omp_get_num_threads(void) int omp_get_thread_num(void) int omp_set_num_threads(int NumThreads) OpenMP并行化的开销,负载平衡,线程同步开销等方面都是影响执行性能的原因。四、实验原理OpenMP应用编程接口API是在共享存储体系结构上的一个编程模型,其包含:编译制导(Compiler Directive)、运行库例程(Runtime Library)和环境变量(Environment Variables

30、) 三大部分,并支持增量并行化(Incremental Parallelization),用于实现并行性运算的优化解决方法。OpenMP 基于 fork-join 的编程模式而设计。OpenMP 程序起初以一条单线程的形式开始运行。如果希望在程序中利用并行,那么就需将额外的线程进行分支,以创建线程组。这些线程在称为“并行区域”的代码区域内并行执行。在并行区域末尾,将等待所有线程全部完成工作,并将其重新结合在一起。那时,最初线程或“主”线程将继续执行,直至遇到下一个并行区域(或程序结束)。OpenMP 的语言结构根据编译器指示而定义,可为编译器布置任务,以实施理想的并行。在 C 和 C+ 中,这

31、些指示根据制导语句来定义。OpenMP 制导语句在任何情况下的形式都相同:#pragma omp construct_name one_or_more_clauses其中“construct_name”规定了希望执行的并行动作,而“clauses”则对该动作进行修改,或对线程所见的数据环境进行控制。 OpenMP 是一种显式的并行编程语言。一旦线程创建,或者工作已经映射到该线程上,那么必须对希望执行的动作加以详述。因此,即使是 OpenMP 这样简单的 API 也有诸多结构和子句需要学习。所幸的是,仅利用整个 OpenMP 语言的一小部分子集,便可完成大量上述工作。可利用“parallel”结

32、构在 OpenMP 中创建线程。#pragma omp parallelA block of statements独自使用时(没有修改任何子句),程序可创建出一系列线程供运行时环境选择(这些线程通常与处理器或内核的数量相等)。每条线程都将根据并行制导语句来执行语句块。该语句块可以是 C 中的任意合法语句组,但是唯一例外的是:不能分支到并行语句块之中或之外。如果线程要全面执行语句组,并且该程序的继发行为还要有意义,那么便不能随意将线程分支到并行区域内的结构之中或之外。这是 OpenMP 的一项公共约束。将这种缺乏某些分支的语句块称为“结构块”。可以令所有线程执行相同的语句,从而进行大量的并行编程

33、。但是要体验 OpenMP 的全部功能,要做的就不止这些。需要在多条线程之间共享执行语句集的工作。这种方式称为“工作共享”。最常见的工作分享结构是循环结构,在 C 中即为“for”循环#pragma omp for但是,这一结构仅对具有规范形式的简单循环起作用for(i=lower_limit; iupper_limit; inc_exp)“for”结构执行循环的迭代,并将其打包至那些利用并行结构创建的早期线程组中。循环极限和循环索引 (inc_exp) 的递增表达式需在编译时完全确定,并且这些符号中使用的任何恒量都必须在线程组中保持相同。系统需要得出循环的迭代数量,然后将其映射到能够分发至线

34、程组的集上。如果所有线程均计算相同的索引集,那么上述工作只有通过持续稳定的方式才能实现。请注意,“for”结构并不能创建线程,只能借助并行结构来做到这点。为了简捷起见,可以将并行结构和“for”结构放在一个制导语句中。#pragma omp parallel for此举可创建一个线程组,以便执行紧随其后的循环迭代。该循环迭代必须是独立的,因此不论迭代的执行顺序如何,或是究竟由哪些线程执行循环的哪些迭代部分,循环结果都将相同。如果一条线程写入变量,另一条线程读取变量,那么将产生循环传递相关性 (loop-carried dependence),程序也将生成错误的结果。编程人员必须仔细分析循环体,

35、以确保没有任何循环传递相关性的发生。在很多情况下,循环传递相关性来源于保存中间结果(用于指定的循环迭代)的变量。在此情况下,可以通过声明每条线程都将具有自己的变量值,以除去循环传递相关性。这可通过私有子句来实现。例如,如果循环使用名为“tmp”的变量来保存临时值,那么可将以下子句添加到 OpenMP 结构中,这样它便可用于循环体内部,而不会造成任何循环传递相关性。private(tmp)另一种常见情况是循环内出现变量,并用于从每个迭代中累积数值。例如,可以利用循环将某项计算的所有结果进行求和,得出一个数值。这在并行编程中十分常见,通常被称为“规约”。在 OpenMP 中,规约子句表示为redu

36、ction(+:sum)同私有子句一样,该子句可添加到 OpenMP 结构中,用以提示编译器等待规约。这时,程序便会创建一个临时私有变量,以便为每条线程计算累积操作的部分结果。当该结构运行到最后时,每条线程的值将结合起来产生最终答案。用于该规约的操作在子句中同样进行了详细说明。在这种情况下,此操作为“+”。根据对遭受质疑的数学操作进行特性识别,OpenMP可定义用于规约的私有变量值。例如,对于“+”来说,该值为零。当然,OpenMP 还有更为复杂的情况,但是借助这两个结构和两个子句,便能够解释如何并行 程序。五、实验内容模块一:基础模块实验例程路径:codeOpenMPHelloworlds1

37、、关闭病毒扫描和监控程序;2、采用Microsoft DevStudio工具新建工程,并加入实验程序文件:Helloworlds.c;3、编译,运行程序并记录实验结果;4、在源程序代码中的找到主程序体:printf(Hello Worldn);for(i=0;iSet OMP_NUM_THREADS=2;7、使用Intel C+编译器重新编译,记录并比较结果有何不同;C:icl /Qopenmp HelloWorlds.c;8、分别运行程序多次,观测实验结果,并记录,分析每次的结果是否相同,为什么?模块二:积分方法求PI值的并行处理化算法实验例程路径:codeOpenMP pi1、关闭病毒扫描

38、和监控程序;2、采用Microsoft DevStudio工具打开实验程序文件:pi.sln;3、编译,运行程序并记录实验结果;4、在源程序代码中的找到主程序体中进行omp方式优化 需要并行运算的程序体:加上#pragma omp parallel段 找到for循环体引入omp并行处理方法加上#pragma omp forfor(xxx:yyy:zzz)段 检查所有变量,将需要进行特别声明的变量进行omp处理:#pragma omp parallel private(varname,vaname)reduction(+:varname,varname) shared(varname,varna

39、me)段 对于特殊的共享变量,进行加锁处理pragma omp critical段5、使用/Qopenmp参数重新编译程序;6、设定Openmp线程数:C:Set OMP_NUM_THREADS=?;7、重新运行程序,观测实验结果,并记录,分析采用OpenMP前后程序性能差异;8、程序修改位置及修改依据;9、分析比较程序pi_serial.c和pi.c的差异。模块三:PI值蒙特卡洛算法的改进与编程实验例程路径:codeOpenMPMontecarlopi1、关闭病毒扫描和监控程序;2、采用Microsoft DevStudio工具打开实验程序文件:Montecarlopi.sln;3、编译,运

40、行程序并记录实验结果;4、在源程序代码中的找到主程序体中进行omp方式优化 需要并行运算的程序体:加上#pragma omp parallel段 找到for循环体引入omp并行处理方法加上#pragma omp forfor(xxx:yyy:zzz)段 检查所有变量,将需要进行特别声明的变量进行omp处理:#pragma omp parallel private(varname,vaname)reduction(+:varname,varname) shared(varname,varname)段 对于特殊的共享变量,进行加锁处理 pragma omp critical 段5、使用/Qopen

41、mp参数重新编译程序;6、设定Openmp线程数:C:Set OMP_NUM_THREADS=?;7、重新运行程序,观测实验结果,并记录;8、程序修改位置及修改依据。9、分析比较程序pimonte_VSL_serial.c和pimonte_VSL.c的差异六、实验结果及分析(附代码)模块一:基础练习本实验是基础练习模块。 实验步骤: 用Microsoft Visual Studio 2005创建控制台项目HelloThreads 。 创建HelloThreads.cpp文件,内容如下: (1)简答与思考:1. 写出关键的并行代码(1) 四个线程各自执行6次迭代。/ 0022.OpenMP_2.

42、cpp : 定义?控?制?台应|用?程序的?入?口点?。/wirtten by Cosmos Wang/2010-12-16#include stdafx.h#include int _tmain(int argc, _TCHAR* argv)printf(Hello World n);#pragma omp parallelfor(int i=0; i6; i+)printf(Iter:%d Thread%dn ,i,omp_get_thread_num();printf(GoodBye Worldn);return 0;(2)(2) 四个线程协同完成6次迭代。/ 0022.OpenMP_2

43、.cpp : 定义?控?制?台应|用?程序的?入?口点?。/wirtten by Cosmos Wang/2010-12-16#include stdafx.h#include int _tmain(int argc, _TCHAR* argv)printf(Hello World n);#pragma omp parallel#pragma omp forfor(int i=0; i6; i+)printf(Iter:%d Thread%dn ,i,omp_get_thread_num();printf(GoodBye Worldn);return 0;(3)2. 附加练习:(1) 编译执行

44、下面的代码,写出两种可能的执行结果和一种不可能的执行结果。int i=0,j = 0;#pragma omp parallel for for ( i= 2; i 7; i+ ) for ( j= 3; j 5; j+ ) printf(“i = %d, j = %dn”, i, j); (4)(5)(2) 编译执行下面的代码,写出两种可能的执行结果和一种不可能的执行结果。 int i=0,j = 0;for ( i= 2; i 7; i+ ) #pragma omp parallel for for ( j= 3; j 5; j+ ) printf(“i = %d, j = %dn”, i, j);(6)(3) 分析上述两段代码的不同并行效果。(1)代码,” #pragma omp parallel for”为并行区域只对外层循环起作用,因此外层循环 i 值出现的比较随机。而” #pragma omp parallel for”对内层的循环不起作用,执行方式仍然是串行方式,于是内层循环的出现还是固定的 先3 后4。(2)代码” #pragma omp parallel for”只对内

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论