Freigeben über


A. Beispiele

Nachfolgend finden Sie Beispiele für die in diesem Dokument definierten Konstrukte. Eine Anweisung (Statement), die einer Anweisung (Directive) folgt, ist nur verbunden, sofern dies erforderlich ist, und eine nicht zusammengesetzte Anweisung (Statement) wird von einer direkt vorangehenden Anweisung (Directive) eingerückt.

A.1 Eine einfache Schleife in paralleler Ausführung

Im folgenden Beispiel wird gezeigt, wie man eine Schleife mit der parallel for-Anweisung parallelisieren kann. Die Schleifeniterationsvariable ist standardmäßig privat, daher ist es nicht erforderlich, sie explizit in einer private-Klausel anzugeben.

#pragma omp parallel for
    for (i=1; i<n; i++)
        b[i] = (a[i] + a[i-1]) / 2.0;

A.2 Bedingte Kompilierung

Die folgenden Beispiele veranschaulichen die Verwendung der bedingten Kompilierung mithilfe des OpenMP-Makros _OPENMP. Durch OpenMP-Kompilierung wird das _OPENMP-Makro definiert.

# ifdef _OPENMP
    printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif

Mithilfe des definierten Präprozessoroperators können mehrere Makros in einer einzigen Anweisung getestet werden.

# if defined(_OPENMP) && defined(VERBOSE)
    printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif

A.3 Parallele Bereiche

Die parallel-Anweisung kann in gröberen parallelen Programmen verwendet werden. Im folgenden Beispiel entscheidet jeder Thread im parallelen Bereich anhand der Threadnummer, an welchem Teil des globalen x-Arrays gearbeitet werden soll:

#pragma omp parallel shared(x, npoints) private(iam, np, ipoints)
{
    iam = omp_get_thread_num();
    np =  omp_get_num_threads();
    ipoints = npoints / np;
    subdomain(x, iam, ipoints);
}

A.4 Die nowait-Klausel

Wenn es innerhalb eines parallelen Bereichs viele unabhängige Schleifen gibt, können Sie die nowait Klausel verwenden, um die implizierte Barriere am Ende der for-Anweisung folgendermaßen zu vermeiden:

#pragma omp parallel
{
    #pragma omp for nowait
        for (i=1; i<n; i++)
             b[i] = (a[i] + a[i-1]) / 2.0;
    #pragma omp for nowait
        for (i=0; i<m; i++)
            y[i] = sqrt(z[i]);
}

A.5 Die kritische Anweisung

Das folgende Beispiel enthält mehrere critical-Anweisung. Das Beispiel veranschaulicht ein Warteschlangenmodell, in dem eine Aufgabe aus der Warteschlange herausgenommen und bearbeitet wird. Damit nicht viele Threads dieselbe Aufgabe aus der Queue entfernen, muss der Dequeuing-Vorgang im critical-Abschnitt erfolgen. Da die beiden Warteschlangen in diesem Beispiel unabhängig sind, werden sie durch critical-Anweisungen mit unterschiedlichen Namen geschützt: xaxis und yaxis.

#pragma omp parallel shared(x, y) private(x_next, y_next)
{
    #pragma omp critical ( xaxis )
        x_next = dequeue(x);
    work(x_next);
    #pragma omp critical ( yaxis )
        y_next = dequeue(y);
    work(y_next);
}

A.6 Die lastprivate-Klausel

Die korrekte Ausführung hängt manchmal vom Wert ab, den die letzte Iteration einer Schleife einer Variablen zuweist. Solche Programme müssen alle Variablen wie Argumente einer lastprivate-Klausel auflisten, sodass die Werte der Variablen identisch mit dem Wert bei sequenzieller Ausführung der Schleife sind.

#pragma omp parallel
{
   #pragma omp for lastprivate(i)
      for (i=0; i<n-1; i++)
         a[i] = b[i] + b[i+1];
}
a[i]=b[i];

Im vorherigen Beispiel entspricht der Wert von i am Ende des parallelen Bereichs n-1, genau wie bei sequenzieller Ausführung.

A.7 Die Reduktionsklausel

Das folgende Beispiel veranschaulicht die Reduktion-Klausel:

#pragma omp parallel for private(i) shared(x, y, n) \
                         reduction(+: a, b)
    for (i=0; i<n; i++) {
        a = a + x[i];
        b = b + y[i];
    }

A.8 Parallele Abschnitte

Im folgenden Beispiel (für Abschnitt 2.4.2) können die xaxis-, yaxis-und zaxis-Funktionen gleichzeitig ausgeführt werden. Die erste section-Anweisung ist optional. Alle section-Anweisungen müssen im lexikalischen Umfang des parallel sections Konstrukts erscheinen.

#pragma omp parallel sections
{
    #pragma omp section
        xaxis();
    #pragma omp section
        yaxis();
    #pragma omp section
        zaxis();
}

A.9 Einzelanweisungen

Im folgenden Beispiel wird die Verwendung der single-Anweisung veranschaulicht. Im Beispiel druckt nur ein Thread die Statusmeldung (in der Regel der erste Thread, der auf die single-Anweisung trifft). Der Benutzer darf keine Annahmen darüber treffen, in welchem Thread der single-Abschnitt ausgeführt wird. Alle anderen Threads überspringen den single-Abschnitt und stoppen am Ende des single-Konstrukts an der Barriere. Wenn andere Threads fortgesetzt werden können, ohne auf den Thread zu warten, der den single-Abschnitt ausführt, kann eine nowait-Klausel für die single-Anweisung angegeben werden.

#pragma omp parallel
{
    #pragma omp single
        printf_s("Beginning work1.\n");
    work1();
    #pragma omp single
        printf_s("Finishing work1.\n");
    #pragma omp single nowait
        printf_s("Finished work1 and beginning work2.\n");
    work2();
}

A.10 Sequenzielle Reihenfolge

Sortierte Abschnitte sind für die sequenzielle Anordnung der Ausgabe von Arbeit nützlich, die parallel ausgeführt wird. Das folgende Programm druckt die Indizes in sequenzieller Reihenfolge aus:

#pragma omp for ordered schedule(dynamic)
    for (i=lb; i<ub; i+=st)
        work(i);
void work(int k)
{
    #pragma omp ordered
        printf_s(" %d", k);
}

A.11 Eine festgelegte Anzahl von Threads

Einige Programme basieren auf einer fest vordefinierten Anzahl von Threads, die ordnungsgemäß ausgeführt werden müssen. Da die Standardeinstellung für die dynamische Anpassung der Anzahl von Threads im Rahmen der Implementierung definiert wird, können solche Programme die „Dynamische Threads“-Funktion deaktivieren und die Anzahl der Threads explizit festlegen, um portabel zu bleiben. Das folgende Beispiel zeigt, wie Sie dies mithilfe von omp_set_dynamicund omp_set_num_threads erreichen können:

omp_set_dynamic(0);
omp_set_num_threads(16);
#pragma omp parallel shared(x, npoints) private(iam, ipoints)
{
    if (omp_get_num_threads() != 16)
      abort();
    iam = omp_get_thread_num();
    ipoints = npoints/16;
    do_by_16(x, iam, ipoints);
}

In diesem Beispiel wird das Programm nur dann ordnungsgemäß ausgeführt, wenn es von 16 Threads ausgeführt wird. Wenn die Implementierung nicht in der Lage ist, 16 Threads zu unterstützen, wird das Verhalten in diesem Beispiel durch die Implementierung definiert.

Die Anzahl der Threads, die einen parallelen Bereich ausführen, bleibt während eines parallelen Bereichs unabhängig von der Einstellung für dynamische Threads konstant. Der Mechanismus für dynamische Threads bestimmt die Anzahl der Threads, die zu Beginn des parallelen Bereichs verwendet werden sollen, und hält sie für die Dauer des Bereichs konstant.

A.12 Die Atomic-Anweisung

Im folgenden Beispiel werden Racebedingungen (gleichzeitige Aktualisierungen eines Elements von x durch viele Threads) mithilfe der atomic-Anweisung vermieden:

#pragma omp parallel for shared(x, y, index, n)
    for (i=0; i<n; i++)
    {
        #pragma omp atomic
            x[index[i]] += work1(i);
        y[i] += work2(i);
    }

Der Vorteil der Verwendung der atomic-Anweisung in diesem Beispiel besteht darin, dass Aktualisierungen zweier verschiedener Elemente von x parallel erfolgen können. Wenn stattdessen eine critical-Anweisung verwendet wird, werden alle Aktualisierungen von Elementen von x seriell ausgeführt (jedoch nicht in einer garantierten Reihenfolge).

Die atomic-Anweisung gilt nur für die C- oder C++-Anweisung, die unmittelbar auf sie folgt. Daher werden Elemente von y in diesem Beispiel nicht atomisch aktualisiert.

A.13 Eine Flush-Anweisung mit einer Liste

Im folgenden Beispiel wird die flush-Anweisung für die Punkt-zu-Punkt-Synchronisierung bestimmter Objekte zwischen Threadpaaren verwendet:

int   sync[NUMBER_OF_THREADS];
float work[NUMBER_OF_THREADS];
#pragma omp parallel private(iam,neighbor) shared(work,sync)
{
    iam = omp_get_thread_num();
    sync[iam] = 0;
    #pragma omp barrier

    // Do computation into my portion of work array
    work[iam] = ...;

    //  Announce that I am done with my work
    // The first flush ensures that my work is
    // made visible before sync.
    // The second flush ensures that sync is made visible.
    #pragma omp flush(work)
    sync[iam] = 1;
    #pragma omp flush(sync)

    // Wait for neighbor
    neighbor = (iam>0 ? iam : omp_get_num_threads()) - 1;
    while (sync[neighbor]==0)
    {
        #pragma omp flush(sync)
    }

    // Read neighbor's values of work array
    ... = work[neighbor];
}

A.14 Eine flush-Anweisung ohne eine Liste

Im folgenden Beispiel (für Abschnitt 2.6.5) wird zwischen den durch eine flush-Anweisung betroffenen gemeinsamen Objekten mit keiner Liste und den nicht betroffenen gemeinsamen Objekten unterschieden.

// omp_flush_without_list.c
#include <omp.h>

int x, *p = &x;

void f1(int *q)
{
    *q = 1;
    #pragma omp flush
    // x, p, and *q are flushed
    //   because they are shared and accessible
    // q is not flushed because it is not shared.
}

void f2(int *q)
{
    #pragma omp barrier
    *q = 2;

    #pragma omp barrier
    // a barrier implies a flush
    // x, p, and *q are flushed
    //   because they are shared and accessible
    // q is not flushed because it is not shared.
}

int g(int n)
{
    int i = 1, j, sum = 0;
    *p = 1;

    #pragma omp parallel reduction(+: sum) num_threads(10)
    {
        f1(&j);
        // i, n and sum were not flushed
        //   because they were not accessible in f1
        // j was flushed because it was accessible
        sum += j;
        f2(&j);
        // i, n, and sum were not flushed
        //   because they were not accessible in f2
        // j was flushed because it was accessible
        sum += i + j + *p + n;
    }
    return sum;
}

int main()
{
}

A.15 Die Anzahl verwendeter Threads

Betrachten Sie das folgende fehlerhafte Beispiel (für Abschnitt 3.1.2):

np = omp_get_num_threads(); // misplaced
#pragma omp parallel for schedule(static)
    for (i=0; i<np; i++)
        work(i);

Der omp_get_num_threads()-Aufruf gibt im seriellen Abschnitt des Codes „1“ zurück, sodass np immer gleich „1“ im vorherigen Beispiel ist. Um die Anzahl der Threads zu ermitteln, die für den parallelen Bereich bereitgestellt werden, muss sich der Aufruf innerhalb des parallelen Bereichs befinden.

Das folgende Beispiel veranschaulicht, wie Sie dieses Programm umschreiben können, ohne eine Abfrage für die Anzahl der Threads einzuschließen:

#pragma omp parallel private(i)
{
    i = omp_get_thread_num();
    work(i);
}

A.16 Sperren

Im folgenden Beispiel (für Abschnitt 3.2) muss das Argument für die Lock-Funktionen den Typ omp_lock_t haben, und es ist nicht erforderlich, es zu flushen. Die Sperrfunktionen bewirken, dass sich die Threads im Leerlauf befinden, während sie auf den Eintritt in den ersten kritischen Abschnitt warten, jedoch andere Arbeit verrichten, während sie auf den Eintritt in den zweiten kritischen Abschnitt warten. Die omp_set_lock-Funktion blockert, die omp_test_lock-Funktion aber nicht, sodass die Arbeit in skip() ausgeführt werden kann.

// omp_using_locks.c
// compile with: /openmp /c
#include <stdio.h>
#include <omp.h>

void work(int);
void skip(int);

int main() {
   omp_lock_t lck;
   int id;

   omp_init_lock(&lck);
   #pragma omp parallel shared(lck) private(id)
   {
      id = omp_get_thread_num();

      omp_set_lock(&lck);
      printf_s("My thread id is %d.\n", id);

      // only one thread at a time can execute this printf
      omp_unset_lock(&lck);

      while (! omp_test_lock(&lck)) {
         skip(id);   // we do not yet have the lock,
                     // so we must do something else
      }
      work(id);     // we now have the lock
                    // and can do the work
      omp_unset_lock(&lck);
   }
   omp_destroy_lock(&lck);
}

A.17 Schachtelbare Verriegelungen

Im folgenden Beispiel (für Abschnitt 3.2) wird veranschaulicht, wie eine verschachtelbare Sperre verwendet werden kann, um Aktualisierungen sowohl an einer gesamten Struktur als auch an einem ihrer Mitglieder zu synchronisieren.

#include <omp.h>
typedef struct {int a,b; omp_nest_lock_t lck;} pair;

void incr_a(pair *p, int a)
{
    // Called only from incr_pair, no need to lock.
    p->a += a;
}

void incr_b(pair *p, int b)
{
    // Called both from incr_pair and elsewhere,
    // so need a nestable lock.

    omp_set_nest_lock(&p->lck);
    p->b += b;
    omp_unset_nest_lock(&p->lck);
}

void incr_pair(pair *p, int a, int b)
{
    omp_set_nest_lock(&p->lck);
    incr_a(p, a);
    incr_b(p, b);
    omp_unset_nest_lock(&p->lck);
}

void f(pair *p)
{
    extern int work1(), work2(), work3();
    #pragma omp parallel sections
    {
        #pragma omp section
            incr_pair(p, work1(), work2());
        #pragma omp section
            incr_b(p, work3());
    }
}

A.18 Geschachtelte for-Direktiven

Das folgende Beispiel der Verschachtelung von forAnweisungen ist konform, da die innere und die äußere for-Anweisung an verschiedene parallele Bereiche gebunden sind.

#pragma omp parallel default(shared)
{
    #pragma omp for
        for (i=0; i<n; i++)
        {
            #pragma omp parallel shared(i, n)
            {
                #pragma omp for
                    for (j=0; j<n; j++)
                        work(i, j);
            }
        }
}

Die folgende Variante des vorherigen Beispiels ist ebenfalls konform:

#pragma omp parallel default(shared)
{
    #pragma omp for
        for (i=0; i<n; i++)
            work1(i, n);
}

void work1(int i, int n)
{
    int j;
    #pragma omp parallel default(shared)
    {
        #pragma omp for
            for (j=0; j<n; j++)
                work2(i, j);
    }
    return;
}

A.19 Beispiele für eine falsche Verschachtelung von Arbeitsteilungsdirektiven

Die Beispiele in diesem Abschnitt veranschaulichen die Regeln zur Schachtelung von Anweisungen.

Das folgende Beispiel ist nicht konform, da die inneren und äußeren for-Anweisungen geschachtelt und an dieselbe parallel-Anweisung gebunden sind:

void wrong1(int n)
{
  #pragma omp parallel default(shared)
  {
      int i, j;
      #pragma omp for
      for (i=0; i<n; i++) {
          #pragma omp for
              for (j=0; j<n; j++)
                 work(i, j);
     }
   }
}

Die folgende dynamisch geschachtelte Version des vorherigen Beispiels ist ebenfalls nicht konform:

void wrong2(int n)
{
  #pragma omp parallel default(shared)
  {
    int i;
    #pragma omp for
      for (i=0; i<n; i++)
        work1(i, n);
  }
}

void work1(int i, int n)
{
  int j;
  #pragma omp for
    for (j=0; j<n; j++)
      work2(i, j);
}

Das folgende Beispiel ist nicht konform, da die for- und single-Anweisungen geschachtelt und an denselben parallelen Bereich gebunden sind:

void wrong3(int n)
{
  #pragma omp parallel default(shared)
  {
    int i;
    #pragma omp for
      for (i=0; i<n; i++) {
        #pragma omp single
          work(i);
      }
  }
}

Das folgende Beispiel ist nicht konform, da eine barrier-Anweisung innerhalb einer for-Anweisung zu einem Deadlock führen kann:

void wrong4(int n)
{
  #pragma omp parallel default(shared)
  {
    int i;
    #pragma omp for
      for (i=0; i<n; i++) {
        work1(i);
        #pragma omp barrier
        work2(i);
      }
  }
}

Das folgende Beispiel ist nicht konform, da die barrier-Anweisung zu einem Deadlock führt, weil zur gleichen Zeit nur ein Thread in den critical-Abschnitt eintreten kann:

void wrong5()
{
  #pragma omp parallel
  {
    #pragma omp critical
    {
       work1();
       #pragma omp barrier
       work2();
    }
  }
}

Das folgende Beispiel ist nicht konform, da die barrier-Anweisung zu einem Deadlock führt, weil nur ein Thread den single-Abschnitt ausführt:

void wrong6()
{
  #pragma omp parallel
  {
    setup();
    #pragma omp single
    {
      work1();
      #pragma omp barrier
      work2();
    }
    finish();
  }
}

A.20 Bindung von Barrieredirektiven

Die Regeln für Direktivenbindungen verlangen, dass eine barrier-Direktive an die nächstgelegene, sie einschließende parallel-Direktive gebunden wird. Weitere Informationen zur Anweisungsbindung finden Sie im Abschnitt 2.8.

Im folgenden Beispiel ist der Aufruf von main an sub2 regelkonform, da die barrier-Anweisung (in sub3) an den parallelen Bereich in sub2 gebunden ist. Der Aufruf von main an sub1 ist konform, da die barrier-Anweisung an den parallelen Bereich in der Subroutine sub2 bindet. Der Aufruf von main an sub3 ist kompatibel, da die barrier-Anweisung nicht an einen parallelen Bereich gebunden und ignoriert wird. Außerdem synchronisiert die barrier-Anweisung nur das Team von Threads im einschließenden parallelen Bereich und nicht alle Threads, die in sub1erstellt wurden.

int main()
{
    sub1(2);
    sub2(2);
    sub3(2);
}

void sub1(int n)
{
    int i;
    #pragma omp parallel private(i) shared(n)
    {
        #pragma omp for
        for (i=0; i<n; i++)
            sub2(i);
    }
}

void sub2(int k)
{
     #pragma omp parallel shared(k)
     sub3(k);
}

void sub3(int n)
{
    work(n);
    #pragma omp barrier
    work(n);
}

A.21 Bereichsvariablen mithilfe der private-Klausel

Die Werte von i und j im folgenden Beispiel sind beim Beenden vom parallelen Bereich nicht definiert:

int i, j;
i = 1;
j = 2;
#pragma omp parallel private(i) firstprivate(j)
{
  i = 3;
  j = j + 2;
}
printf_s("%d %d\n", i, j);

Weitere Informationen zur private-Klausel finden Sie im Abschnitt 2.7.2.1.

A.22 Die default(none)-Klausel

Im folgenden Beispiel werden die Variablen unterschieden, die von der default(none)-Klausel betroffen bzw. nicht betroffen sind:

// openmp_using_clausedefault.c
// compile with: /openmp
#include <stdio.h>
#include <omp.h>

int x, y, z[1000];
#pragma omp threadprivate(x)

void fun(int a) {
   const int c = 1;
   int i = 0;

   #pragma omp parallel default(none) private(a) shared(z)
   {
      int j = omp_get_num_thread();
             //O.K.  - j is declared within parallel region
      a = z[j];       // O.K.  - a is listed in private clause
                      //      - z is listed in shared clause
      x = c;          // O.K.  - x is threadprivate
                      //      - c has const-qualified type
      z[i] = y;       // C3052 error - cannot reference i or y here

      #pragma omp for firstprivate(y)
         for (i=0; i<10 ; i++) {
            z[i] = y;  // O.K. - i is the loop control variable
                       // - y is listed in firstprivate clause
          }
       z[i] = y;   // Error - cannot reference i or y here
   }
}

Weitere Informationen zur default-Klausel finden Sie im Abschnitt 2.7.2.5.

A.23 Beispiele für die ordered-Direktive

Man kann viele geordnete Abschnitte mit einer for angeben, die durch die ordered-Klausel spezifiziert werden. Das erste Beispiel ist nicht konform, da die API die folgende Regel angibt:

„Eine Iteration einer Schleife mit einem for-Konstrukt darf nicht mehr als einmal dieselbe ordered-Anweisung ausführen, und sie darf nicht mehr als eine ordered-Anweisung ausführen.“ (Weitere Informationen finden Sie im Abschnitt 2.6.6.5.)

In diesem nicht konformem Beispiel führen alle Iterationen zwei sortierte Abschnitte aus:

#pragma omp for ordered
for (i=0; i<n; i++)
{
    ...
    #pragma omp ordered
    { ... }
    ...
    #pragma omp ordered
    { ... }
    ...
}

Das folgende konforme Beispiel zeigt ein for mit mehr als einem nummerierten Abschnitt.

#pragma omp for ordered
for (i=0; i<n; i++)
{
    ...
    if (i <= 10)
    {
        ...
        #pragma omp ordered
        { ... }
    }
    ...
    (i > 10)
    {
        ...
        #pragma omp ordered
        { ... }
    }
    ...
}

A.24 Beispiel für die private-Klausel

Die private-Klausel eines parallelen Bereichs gilt nur für den lexikalische Umfang des Bereichs, nicht für den dynamischen. Daher bezieht sich im folgenden Beispiel jede Verwendung der Variablen a innerhalb der for-Schleife in der Routine f auf eine private Kopie der Variablen a, während eine Verwendung in der Routine g auf die globale Variable averweist.

int a;

void f(int n)
{
    a = 0;

    #pragma omp parallel for private(a)
    for (int i=1; i<n; i++)
    {
        a = i;
        g(i, n);
        d(a);     // Private copy of "a"
        ...
    }
    ...

void g(int k, int n)
{
    h(k,a); // The global "a", not the private "a" in f
}

A.25 Beispiele für die copyprivate-Datenattributklausel

Beispiel 1: Die copyprivate-Klausel kann verwendet werden, um von einem einzelnen Thread abgerufene Werte direkt an alle Instanzen der privaten Variablen in den anderen Threads zu übertragen.

float x, y;
#pragma omp threadprivate(x, y)

void init( )
{
    float a;
    float b;

    #pragma omp single copyprivate(a,b,x,y)
    {
        get_values(a,b,x,y);
    }

    use_values(a, b, x, y);
}

Wenn die Routine init aus einem seriellen Bereich aufgerufen wird, wird das Verhalten nicht durch das Vorhandensein der Anweisungen beeinflusst. Nachdem der Aufruf der get_values-Routine von einem Thread ausgeführt wurde, verlässt kein Thread das Konstrukt, bis die privaten Objekte, die durch a, b, xund y in allen Threads definiert wurden, mit den gelesenen Werten definiert wurden.

Beispiel 2: Nehmen wir im Gegensatz zum vorherigen Beispiel an, dass der Lesevorgang von einem bestimmten Thread ausgeführt werden muss, z. B. dem Masterthread. In diesem Fall kann die copyprivate-Klausel nicht verwendet werden, um die Übertragung direkt zu erledigen. Sie kann jedoch genutzt werden, um den Zugriff auf ein temporär freigegebenes Objekt bereitzustellen.

float read_next( )
{
    float * tmp;
    float return_val;

    #pragma omp single copyprivate(tmp)
    {
        tmp = (float *) malloc(sizeof(float));
    }

    #pragma omp master
    {
        get_float( tmp );
    }

    #pragma omp barrier
    return_val = *tmp;
    #pragma omp barrier

    #pragma omp single
    {
       free(tmp);
    }

    return return_val;
}

Beispiel 3: Angenommen, die Anzahl der in einem parallelen Bereich erforderlichen Lock-Objekte kann vor dem Eintreten nicht einfach bestimmt werden. Die copyprivate-Klausel kann verwendet werden, um Zugriff auf geteilte Lock-Objekte zu bieten, die innerhalb dieses parallelen Bereichs zugeordnet sind.

#include <omp.h>

omp_lock_t *new_lock()
{
    omp_lock_t *lock_ptr;

    #pragma omp single copyprivate(lock_ptr)
    {
        lock_ptr = (omp_lock_t *) malloc(sizeof(omp_lock_t));
        omp_init_lock( lock_ptr );
    }

    return lock_ptr;
}

A.26 Die threadprivate-Anweisung

Die folgenden Beispiele veranschaulichen die Verwendung der threadprivate--Anweisung, um jedem Thread einen separaten Zähler zuzuweisen.

Beispiel 1

int counter = 0;
#pragma omp threadprivate(counter)

int sub()
{
    counter++;
    return(counter);
}

Beispiel 2

int sub()
{
    static int counter = 0;
    #pragma omp threadprivate(counter)
    counter++;
    return(counter);
}

A.27 C99-Arrays mit variabler Länge

Im folgenden Beispiel wird veranschaulicht, wie C99-Arrays variabler Länge (Variable Length Arrays, VLAs) in einer firstprivate-Anweisung verwendet werden.

Hinweis

Arrays mit variabler Länge werden in Visual C++ derzeit nicht unterstützt.

void f(int m, int C[m][m])
{
    double v1[m];
    ...
    #pragma omp parallel firstprivate(C, v1)
    ...
}

A.28 Die num_threads-Klausel

Das folgende Beispiel veranschaulicht die num_threads-Klausel. Der parallele Bereich wird mit maximal zehn Threads ausgeführt.

#include <omp.h>
int main()
{
    omp_set_dynamic(1);
    ...
    #pragma omp parallel num_threads(10)
    {
        ... parallel region ...
    }
}

A.29 Konstrukt zur Arbeitsteilung innerhalb eines kritischen Konstrukts

Im folgenden Beispiel wird die Verwendung eines Arbeitsteilungskonstrukts innerhalb eines critical-Konstrukts veranschaulicht. Dieses Beispiel ist konform, da das Arbeitsaufteilungskonstrukt und das critical-Konstrukt nicht an denselben parallelen Bereich gebunden sind.

void f()
{
  int i = 1;
  #pragma omp parallel sections
  {
    #pragma omp section
    {
      #pragma omp critical (name)
      {
        #pragma omp parallel
        {
          #pragma omp single
          {
            i++;
          }
        }
      }
    }
  }
}

A.30 Erneute Privatisierung

Im folgenden Beispiel wird die erneute Privatisierung von Variablen veranschaulicht. Private Variablen können in einer geschachtelten Anweisung erneut als private markiert werden. Sie müssen diese Variablen nicht im eingeschlossenen parallelen Bereich freigeben.

int i, a;
...
#pragma omp parallel private(a)
{
  ...
  #pragma omp parallel for private(a)
  for (i=0; i<10; i++)
     {
       ...
     }
}

A.31 Threadsichere Lock-Funktionen

Im folgenden C++-Beispiel wird das Initialisieren eines Arrays von Locks in einem parallelen Bereich mithilfe von omp_init_lock veranschaulicht.

// A_13_omp_init_lock.cpp
// compile with: /openmp
#include <omp.h>

omp_lock_t *new_locks() {
   int i;
   omp_lock_t *lock = new omp_lock_t[1000];
   #pragma omp parallel for private(i)
   for (i = 0 ; i < 1000 ; i++)
      omp_init_lock(&lock[i]);

   return lock;
}

int main () {}