簡體   English   中英

多線程C ++應用程序中的Fortran 77通用塊

[英]Fortran 77 common blocks in multithreading C++ application

我開發了一個C ++程序,該程序調用了Fortran 77例程。 主C ++程序可能運行多線程。 但是,碰巧Fortran 77例程隱藏了幾個公共塊,這些塊在每次調用時都會根據其參數進行修改。

恐怕所有公共塊都可能在多個線程之間共享,並且對這些塊的並發訪問可能會弄亂一切。

  • 第一個問題 :對嗎? 通用塊會在多個線程之間共享嗎?

  • 第二個問題 :是否有避免這種情況的簡單方法? 重寫Fortran例程似乎負擔不起,我寧願尋找一種方法,使每個線程都有其自己的所有公共塊副本(這些塊不大,應該可以快速復制)。 我不知道編譯選項是否有幫助,或者OpenMP是否可以幫助我。

您認為公共塊不是線程安全的是正確的。 它們是全局數據,可讓您在共享相同存儲關聯的任何作用域中聲明變量。 如果使用C ++編寫具有所有可能引起的線程同步問題的全局變量,則效果基本相同。

不幸的是,我認為沒有一種簡單的方法可以避免這種情況。 如果您需要維護多線程方法,那么過去我曾提出過一個想法,就是將所有變量從一個公共塊移到用戶定義的類型中,並將該類型的實例傳遞給任何需要訪問的過程給他們(每個線程一個實例)。 不過,這將涉及對實現的代碼進行潛在的昂貴更改。

您還需要使用Fortran代碼查看其他線程安全問題(這不是詳盡的列表):

  • 每個線程的IO單元應該是唯一的,否則文件輸入/輸出將不可靠
  • 具有SAVE屬性的所有變量(隱含在模塊變量中以及在聲明時初始化的變量中)都是有問題的(這些變量在過程調用之間是持久的)。 此屬性的隱含性還取決於編譯器/標准,這使它成為更大的潛在問題。
  • 使用RECURSIVE屬性聲明過程-這意味着該函數是可重入的。 這也可以通過使用編譯器的openmp選項進行編譯而不是更改代碼來滿足。

您可以探索的另一種方法是使用多處理或消息傳遞來並行化代碼,而不是使用多線程。 這避免了Fortran代碼的線程安全性問題,但又提出了另一種可能代價高昂的代碼體系結構更改。

另請參閱:

是的,共享公共塊。

在OpenMP中,可以將公共塊指定為THREADPRIVATE。 每個線程然后動態地創建公共塊的新實例。 要從原始副本中復制數據,請使用COPYIN說明符。 另請參見OpenMP threadprivate和private之間的區別

基本語法是

!$OMP THREADPRIVATE (/cb/, ...)  

其中cb是公共塊的名稱。 參見https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE

是的,您不能在多線程中使用公共區域。 不,沒有辦法避免這種情況。 鏈接器實際上將所有公共區域合並為單個塊,並且無法在線程之間復制它。 這是存在遺留Fortran代碼的任何地方的已知問題。 最常見的解決方案是使用多處理而不是多線程。

感謝您的回答,尤其是有關OpenMP的提示,它確實是可行的。 為了確保完全正確,我制作了一個小程序。 它由一個主要C ++程序中調用的一個fortran 77部分組成(這是我關心的問題):

fortran 77例程func.f

  subroutine set(ii, jj)
  implicit none

  include "func.inc"
  integer ii, jj
  integer OMP_GET_NUM_THREADS, OMP_GET_THREAD_NUM

  i = ii + 1
  j = jj

  !$OMP CRITICAL
  print *, OMP_GET_THREAD_NUM(), OMP_GET_NUM_THREADS(), i, j
  !$OMP END CRITICAL
  return
  end


  subroutine func(n, v)
  implicit none

  include "func.inc"

  integer n, k
  integer v(n)

  do k = i, j
     a = k + 1
     b = a * a
     c = k - 1
     v(k) = b - c * c
  enddo

  return
  end

與包含文件func.inc

  integer i, j
  integer a, b, c

  common /mycom1/ i, j
  !$OMP THREADPRIVATE(/mycom1/)
  common /mycom2/ a, b, c
  !$OMP THREADPRIVATE(/mycom2/)

最后是C ++程序main.cpp

#include<iostream>
#include<sstream>
#include<vector>
using namespace std;

#include<omp.h>

extern "C"
{
  void set_(int*, int*);
  void func_(int*, int*);
};


int main(int argc, char *argv[])
{
  int nthread;
  {
    istringstream iss(argv[1]);
    iss >> nthread;
  }

  int n;
  {
    istringstream iss(argv[2]);
    iss >> n;
  }

  vector<int> a(n, -1);

#pragma omp parallel num_threads(nthread) shared(a)
  {
    const int this_thread = omp_get_thread_num();
    const int num_threads = omp_get_num_threads();

    const int m = n / num_threads;
    int start = m * this_thread;
    int end = start + m;

    const int p = n % num_threads;
    for (int i = 0; i < this_thread; ++i)
      if (p > i) start++;
    for (int i = 0; i <= this_thread; ++i)
      if (p > i) end++;

#pragma omp critical
    {
      cout << "#t " << this_thread << " : [" << start
           << ", " << end << "[" << endl;
    }

    set_(&start, &end);
    func_(&n, a.data());
  }

  cout << "[ " << a[0];
  for (int i = 1; i < n; ++i)
    cout << ", " << a[i];
  cout << "]" << endl;

  ostringstream oss;
  for (int i = 1; i < n; ++i)
    if ((a[i] - a[i - 1]) != int(4))
      oss << i << " ";

  if (! oss.str().empty())
    cout << "<<!!  Error occured at index " << oss.str()
         << " !!>>" << endl;

  return 0;
}
  • 編譯步驟(gcc版本4.8.1):

     gfortran -c func.f -fopenmp g++ -c main.cpp -std=gnu++11 -fopenmp g++ -o test main.o func.o -lgfortran -fopenmp 
  • 您可以按以下方式啟動它:

     ./test 10 1000 

    哪里

    • 第一個整數(10)是所需的線程數,
    • 第二個(1000)是一個向量的長度。

    該程序的目的是在線程之間拆分此向量,並讓每個線程填充其中的一部分。

    載體的填充在fortran 77中進行:

    • set例程首先設置線程管理的下限和上限,
    • 然后, func例程將填充前一個邊界之間的向量。

通常,如果沒有錯誤,並且不共享常見的fortran 77塊,則最終向量應填充4 * k值,k從1到1000。

我趕不上程序了。 相反,如果我在func.inc中刪除了fortran 77 OMP指令,則通用塊不再是私有的,並且會出現許多錯誤。

綜上所述,要解決我的第一個問題,我唯一需要做的就是將OMP指令添加到任何通用塊的后面,希望這些指令不會太復雜,因為它們都聚集在一個include文件中(例如我的測試)。

希望這會有所幫助。

最好的祝福。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM