186任务分担入口任务分担入口处主要是为组内线程准备好任务分担数据,无论是for、sections还是single语句,它们的入口处都是调用的entering_workshare_region()函数,它们用类型标记_OMP_SECTIONS、_OMP_FOR和_OMP_SINGLE作为区分。下面是sections、for和single各自的入口函数。1./*Thisiscalledintheparser-generatedcode*only*ifweareina2.*parallelregionANDthenumberofthreadsis1.3.*/4.voidort_entering_sections(intnowait,intnumberofsections)sections的入口5.{6.enter_workshare_region(__MYCB,_OMP_SECTIONS,nowait,0,numberofsections,0);7.}1./*Thisiscalledbytheparser-generatedcode,evenifweare2.*notinaparallelregion.3.*/4.voidort_entering_for(intnowait,inthasordered,intlb,intincr,5.ort_gdopt_t*t)for的入口。在代码变换时每个for任务分担域都会定义一个变量t,用于动态和指导调度。6.{7.ort_eecb_t*me=__MYCB;8.9./*Checkifwearenotinaparallelregionortheteamhasonly1thread*/10.if(me-num_siblings==1)只有一个线程可以直接返回11.{12.t-me=(void*)me;13.t-nth=1;14.return;15.}16.enter_workshare_region(me,_OMP_FOR,nowait,hasordered,lb,incr);下面代码用于取得任务分担数据结构的内容,分成nowait和非nowait两种情况17.if(me-nowaitregion)18.{19./*1lesssincemynextNWregionhasbeenincreaseduponentrance*/20.t-data=&(me-parent-workshare.21.REGION(me-mynextNWregion-1).forloop.lb);22.t-lock=(void*)&(me-parent-workshare.23.REGION(me-mynextNWregion-1).reglock);24.}25.else26.{27.t-data=&(me-parent-workshare.blocking.forloop.lb);28.t-lock=(void*)&(me-parent-workshare.blocking.reglock);18729.}30.t-nth=me-num_siblings;31.t-me=me;32.}33.1./*Returns1ifthecurrentthreadshouldexecutetheSINGLEblock2.*/3.intort_mysingle(intnowait)single的入口4.{5.ort_eecb_t*me=__MYCB;6.if(me-num_siblings==1)只有一个线程可以直接返回7.return(1);8.else9.return(enter_workshare_region(me,_OMP_SINGLE,nowait,0,0,0));10.}也就是说这三个任务分担最终将会调用ORT中相同的入口,见图10.3所示。ort_entering_sections()ort_entering_for()ort_entering_single()ort_leaving_sections()ort_leaving_for()ort_leaving_single()entering_workshare_region()leaving_workshare_region()Libort.a静态链接经x_sections.c中的函数xform_sections()、x_for.c中的xform_for()和x_single.c中的xform_single(),插入到OpenMP用户代码中Ort.c中实现变换后的用户代码图10.3任务分担接口函数从编译后的代码中插入的函数ort_entering_sections()、ort_entering_for()或ort_entering_single()里面调用该函数,用于进入到工作分担区域。此时往往有多于1个线程在并行执行。第一个进入并行域的线程负责更新相应的计数器数值并返回1,其他线程则返回0。但是不论是ort_entering_sections()还是ort_entering_for()都不用检查这个返回值,该返回值是给ort_mysingle()函数使用的,以判别是否应该执行single修饰的语句或语句块。传入的参数中wstype是任务分担的类型(sections、for或single),如果是sections类型,那么arg1是section的个数;如果是for类型,那么arg1是for循环的lowerbound,而arg2是循环增量,为将来的ort_entering_for()、ort_entering_sections()和ort_engering_single()做好“记账”(因不涉及具体任务,所以称为记账)准备。关于工作分担的细节,还需要在for语句、sections语句和single语句的处理函数里面完成。下面是该入口函数的代码。1./*Enteraworkshareregion,updatethecorrespondingcounters2.*andreturn1ifiamthefirstthreadtoentertheregion.1883.*Itisguaranteed(bytheparserandtheruntimeroutines)4.*thatwearewithinaparallelregionwith1threads.5.*/6.static7.intenter_workshare_region(ort_eecb_t*me,8.intwstype,intnowait,inthasordered,9.intarg1,/*=#sectionsifwstypeisSECTIONS10.=forloop'slowerboundifFOR*/11.intarg2)/*=theloop'sstepincrementifFOR*/12.{13.ort_workshare_t*ws;14.wsregion_t*r;15.intmyreg,notfirst=1;获取自己的工作共享区。如果是带有nowait子句,那么取active,否则取blocking。REGION通过宏定义为active。16.ws=&(me-parent-workshare);获取组长EECB任务分担域数据指针17.myreg=me-mynextNWregion;mynextNWregion初始值是018.r=(nowait)?&(ws-REGION(myreg)):&(ws-blocking);根据是否阻塞r将指向不同结构19.me-nowaitregion=nowait;/*Rememberthestatusofthisregion*/20.if(nowait)对于出现超过一定数量的活动NOWAIT的代码区时,阻塞等待21.{22./*Iftoomanyworkshareregionsareactive,wemustblockhere23.*untilthetailhasadvancedabit.24.*/25.OMPI_WAIT_WHILE(ws-headregion-ws-tailregion==MAXACTIVEREGIONS&&26.myreg==ws-headregion,YIELD_FREQUENTLY);等待27.(me-mynextNWregion)++;/*Makemereadyforthenextregion*/28.}29.if(!r-empty)return(0);/*Wearenotthefirstforsure*/30.如果有其他线程进来处理过,则直接返回0如果还没有线程处理过这个任务分担域,那么根据类型对for或sections做好准备。因为有可能由多个线程成功检测到前面的(!r-empty)条件造成并发执行下面的代码,需要先对该结构进行加锁。31.ee_set_lock(&r-reglock);/*We'llbeshort*/32.if(r-empty)/*Iamthefirsttoenter*/33.{34.if(wstype==_OMP_SECTIONS)/*Initialize*/对sections的处理35.r-sectionsleft=arg1;arg1是section个数的初始值36.else18937.if(wstype==_OMP_FOR)对for的处理38./*first_to_arrive_at_for(&r-forloop,hasordered,arg1,arg2);*/39.{40.r-forloop.lb=arg1;确定下界41.if(hasordered)确定ordered执行顺序42.{43.r-forloop.ordering.next_iteration=arg1;44.r-forloop.ordering.incr=arg2;45.}46.}47.r-notleft=me-num_siblings;/*Noneleftyet*/全部线程都未离开下面开始对下一区域进行初始化准备(如果还没有初始化)。这样做可以在线程组创建后并行地完成共享区的初始化工作。48./*Now,prepare/initializethenextregion,ifnotalreadyinited.49.*Thisisdonesoastoavoidseriallyinitializingallregionswhen50.*theteamwascreated51.*/52.if(nowait&&myreg+1MAXACTIVEREGIONS)53.{54.if(!ws-REGION(myreg+1).inited)如果未初始化55.{56.ee_init_lock(&ws-REGION(myreg+1).reglock,ORT_LOCK_SPIN);57.ee_init_lock(&ws-REGION(myreg+1).forloop.ordering.lock,ORT_LOCK_SPIN);58.ws-REGION(myreg+1).inited=1;59.}60.ws-REGION(myreg+1).empty=1;61.}62./*Dothislasttoavoidraceswiththreadsthattestwithoutlocking*/63.if(nowait)(ws-headregion)++;/*Advancethehead*/可用结构数量减少1个64.FENCE;65.r-empty=notfirst=0;66.}67.ee_unset_lock(&r-reglock);68.return(!notfirst);69.}退出任务分担退出任务分担域时执行的ort_leaving_sections()或ort_leaving_for()里面将会进一步调用leave_workshare_region(),它的返