2017-08-13 67 views
1

嗨我正在尝试在不同的线程中做一个循环。现在在do循环中我调用一个函数,它再次调用一些子例程并添加到总和中。现在,如果我并行封闭do循环,它会给出随机结果,但是我发现如果将该函数置于CRITICAL环境中,它会给出正确的结果。但是这会花费更多的CPU时间,并不会提高速度。我用一个小测试程序进行了测试,并检查我的逻辑是否正确。然而,在一个大的程序中(我不能在这里发表)只有当我将函数调用放在CRITICAL中时才有效。 下面我给测试程序:(我的测试程序的工作,并在大程序中,我看到funb没有正确采取不同的线程,除非它处于临界环境却给出正确的结果)OpenMP调用函数给出了错误的结果

 sum=0d0 
!$OMP PARALLEL PRIVATE(i,j,sum1,xcn,fun) 
     ithrd=OMP_GET_THREAD_NUM() 
!$OMP DO 
     do i=1,5 
     sum1=0d0 
     do j=1,3 
      xcn=i+j+xx 
!$OMP CRITICAL 
     fun=funb(xnc) 
     write(*,*)fun 
!$OMP END CRITICAL 
     sum1=sum1+fun 
     enddo 
     enddo 
!$OMP END DO 
!$OMP CRITICAL 
     sum=sum+sum1 
!$OMP END CRITICAL 
!$OMP END PARALLEL 
     write(*,*)sum 

如果我在大程序中删除OMP CRITICAL我发现不同的线程在不同的线程中对funb采用相同的值,这应该是不同的。因此,我的理解是:在PARALLEL节中调用的函数有一些限制。如果有人能够澄清这个问题,我会很感激。

功能funb给出为:

 COMPLEX*16 FUNCTION FUNB(ZAA) 

    IMPLICIT COMPLEX*16 (A-H,O-Z) 
    real*8 X1,X2 
    COMMON/ZVAR/ZA 
    COMMON/XVAR/X1,X2 
    ZA=ZAA 
    call myinvini 
    call myinvc(x2,fout) 
    funb=fout 
    RETURN 
    END 

myinvini是WL8,×18一些数据,但再次myinvc是子程序:

subroutine myinvc(x,f2) 
    complex*16 dir,dirc,sta,ss,ssc,cn,cnc,f2,ff,ffc,func 
    complex*16 f22,ans 
    integer igauss,inte,l,m 
    double precision x,range,phi,w,z,zz,zr 
    double precision st,st0,zint,xbl,a,b,dli,sli 
    double precision cpar,zero 
    double precision xl8,wl8,xl32,wl32 
    dimension zint(51) 
    COMMON/iinte/inte 
    complex*16 cbeta 
    common /wgauss/ xl8(8),wl8(8),xl32(32),wl32(32) 
    common /ccpar/ cpar 

    include 'constants.h' 
    igauss = 8 
    zero=0.0d0 
    range=201.0d0 
    phi=3.0d0/4.0d0*pi 
    dir=dcmplx(dcos(phi),dsin(phi)) 
    dirc=dcmplx(dcos(phi),-dsin(phi)) 
    sta=dcmplx(cpar,zero) 
    st =dexp(dlog(range)/dble(inte)) 
    st0=1.0d0 
    zint(1)=zero 
    do 11 l=1,inte  
    st0 =st0*st 
    zint(l+1)=st0-1.0d0 
    11 continue 

    ss=dcmplx(zero,zero) 
    ssc=dcmplx(zero,zero) 
    xbl=dlog(x) 

    do 23 l=1,inte ! inte=5 
    a=zint(l) 
    b=zint(l+1) 
    dli=(b-a)/2.d0 
    sli=(b+a)/2.d0 

    do 24 m=1,igauss 
    if(igauss.eq. 8) w=wl8(m) 
    if(igauss.eq.32) w=wl32(m) 
    if(igauss.eq. 8) zz=xl8(m) 
    if(igauss.eq.32) zz=xl32(m) 
    z =dli*zz+sli 
    cn=sta+z*dir 
    cnc=sta+z*dirc 

    ff=func(cn) 
    ffc=func(cnc) 

    ss=ss+ff*dir*exp(-xbl*cn)*w*dli 
    ssc=ssc+ffc*dirc*exp(-xbl*cnc)*w*dli 
    24 continue 
    23 continue 
    f2=(ss+ssc) 
    return 
    end 
+0

Pleaae向我们展示函数的代码。请关于什么使得函数*线程安全*。这在之前曾多次讨论过。 –

+0

查看https://stackoverflow.com/questions/35347944/fortran-openmp-with-subroutines-and-functions –

+0

@VladimirF我已添加该功能。问题与私人/共享变量的定义有关吗? –

回答

2

在没有threadprivate指令的,公共块变量是共享。并行部分内部引用的函数修改了这样一个公共块变量,这将导致数据竞争,并且openmp标准不允许这样做。

该代码对openmp结构中引用的大多数变量使用隐式键入和隐式指定数据共享属性。从编码风格的角度来看,这些正在悄然兴起。所示的代码有一个可能的变量拼写错误,如果避免了隐含的规格,这可能会被避免。

+0

感谢您的解释。有什么办法可以正确通过公共块吗?哪一个是你所指的变量拼写错误? –

+0

查看有关threadprivate属性的文档。您的示例代码中包含名为'xcn'和'xnc'的变量。不要使用隐式输入! – IanH