2016-03-15 60 views
0

我有一個資源需要在訪問之間保持其狀態。當使用OpenMP並行化程序時,我想確保每個線程都有自己的副本,並且實例不會被銷燬,並且不會爲每個並行區域重新創建實例。爲此,我使用了一個全局變量,即threadprivate。下面,我有一個簡單的測試用例來說明設置。c + +中的OpenMP和資源管理

我有兩個問題:

  1. 能夠保證所有的資源(以下OBJ)是該計劃的執行期間創建的每個線程只有一次?
  2. 當我在四個線程上運行示例程序時,每個線程都會報告「Obj created ...」和「State set to ...」,但只有線程爲零報告「Obj destroyed ...」。這裏發生了什麼?

#ifdef _OPENMP 
#include <omp.h> 
#endif 
#include <vector> 
#include <iostream> 
#include <iomanip> 

class obj { 
public: 
    obj() : state(0) { 
    res = new int [100]; 
#pragma omp critical 
    { 
     std::cout << "Obj created, state " << state; 
#ifdef _OPENMP 
     std::cout << ", thread " << omp_get_thread_num(); 
#endif 
     std::cout << std::endl;  
    } 
    } 

    ~obj() { 
    delete[] res; 
#pragma omp critical 
    { 
    std::cout << "Obj destroyed, state " << state; 
#ifdef _OPENMP 
     std::cout << ", thread " << omp_get_thread_num(); 
#endif 
     std::cout << std::endl;  

    } 
    } 

    void init(int set) { 
    state = set; 
#pragma omp critical 
    { 
     std::cout << "State set to " << state; 
#ifdef _OPENMP 
     std::cout << ", thread " << omp_get_thread_num(); 
#endif 
     std::cout << std::endl;  
    } 
    } 

    int operator()() { 
    return ++state; 
    } 

private: 
    int state; 
    int* res; 
}; 

extern obj obj1; 
#pragma omp threadprivate(obj1) 
obj obj1; 

void init() { 
#ifdef _OPENMP 
#pragma omp parallel 
    { 
    obj1.init(100 * omp_get_thread_num()); 
    } 
#else 
    obj1.init(100); 
#endif 
} 

void work() { 
    std::cout << "Computing" << std::endl; 

    int constexpr length = 20; 
    std::vector<int> vec(length); 

#pragma omp parallel for 
    for (int idx = 0; idx < length; idx++) { 
    vec[idx] = obj1(); 
    } 

    std::cout.fill('0'); 

    for (auto const & e: vec) { 
    std::cout << std::setw(3) << e << ' '; 
    } 
    std::cout << std::endl; 
} 

int main() { 
    init(); 
    work(); 
    work(); 
    work(); 
} 
+0

嗯,我猜測,當obj1超出範圍,(在主結束時我猜想它是全局變量),其他線程已經死了 – Guiroux

回答

2

線程專用下列條件

  • #pragma omp threadprivate下正確工作是本每個聲明的變量;
  • 必須關閉動態線程(默認爲實現定義),並使用omp_set_dynamic(false)

查看示例here

我不會依賴被調用的析構函數。 OpenMP會留下很多未指定的內容,編譯器可能會優化它。

下面是OpenMP的規格(V4.0 p.12.14.2)

threadprivate變量的所有副本根據如何靜態變量的基本語言的處理釋放的儲存摘錄,但在程序中的一個未指定的點。

,其中用於類類型的不同線程專用C++變量任何析構函數的調用順序是不確定的。上

更多信息threadprivate (V4.0 p2.4.12)

一個threadprivate變量的每個副本被初始化一次,由程序指定的方式,但在未指定的點程序在第一次引用該副本之前。變量threadprivate的所有副本的存儲根據如何在基本語言中處理靜態變量而被釋放,但是在程序中的某個未指定的位置。

其中一個線程引用另一個線程副本threadprivate變量的程序不符合。

如果正在執行的線程切換到另一個修改變量的任務,則threadprivate變量的內容可以跨任務調度點更改。有關任務調度的更多詳細信息,請參見第14頁上的第1.3節和第113頁上的第2.11節。

parallel區域中,由主線程引用將在線程中遇到parallel區域的變量的副本。

在一個連續的部分中,引用將是初始線程的變量副本。變量的初始線程副本中的數據值保證在程序中的任何兩個連續的變量引用之間保持不變。

在非初始線程的threadprivate變量數據的值被保證只有在所有以下的條件成立的兩個連續有源parallel區域之間保持的:

  • 既不parallel區域嵌套在另一個明確的parallel地區。

  • 用於執行parallel區域的線程數量是相同的。

  • 用於執行parallel區域的線程關聯策略是相同的。

  • 在封閉任務區域中的DYN-VAR內部控制變量的值是在進入兩個parallel區域

如果這些條件都成立,如果一個threadprivate變量在這兩個區域引用,然後在各自的區域相同的線程數目的線程將引用該變量的同一副本。

+0

謝謝你的答案!你知道爲什麼其他析構函數的輸出沒有打印? – Mankka

+1

OpenMP在線程作用域中爲每個線程創建新變量(例如,使用'thread_local')。當你保持線程數不變時,它不會終止線程。因此這些變量永遠不會超出範圍,並且不會調用析構函數。嘗試調用'omp_set_num_threads(2)',你可能會看到額外的析構函數。 – ftynse

+1

我編輯了答案,引用了一些關於從OpenMP規範破壞的未指定行爲。 – ftynse