Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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 veranschaulicht, wie eine Schleife mithilfe der parallel for-Anweisung parallelisiert wird. 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 critical-Anweisung
Das folgende Beispiel enthält mehrere critical-Anweisung. Das Beispiel veranschaulicht ein Warteschlangenmodell, in dem eine Aufgabe aus der Warteschlange entfernt und bearbeitet wird. Damit nicht viele Threads dieselbe Aufgabe aus der Warteschlange entfernen, muss sich der Entfernungsprozess in einem critical-Abschnitt befinden. 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 reduction-Klausel
Das folgende Beispiel veranschaulicht die reduction-K:
#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 single- Anweisung
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) werden die von einer flush-Anweisung betroffenen freigegebenen Objekte ohne Liste von den nicht betroffenen freigegebenen 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. Leeren ist nicht erforderlich. Die lock-Funktionen bewirken, dass sich die Threads im Leerlauf befinden, während sie auf den Eintrag im ersten critical-Abschnitt warten, jedoch andere Arbeit ausführen, während sie auf den Eintrag in den zweiten 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 Locks
Im folgenden Beispiel (für Abschnitt 3.2) wird veranschaulicht, wie ein verschachtelbares Lock verwendet werden kann, um Aktualisierungen sowohl mit einer gesamten Struktur als auch mit einem seiner Member 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 einer Schachtelung von for-Anweisungen ist konform, da die inneren und äußeren for-Anweisungen 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 Arbeitsteilungsanweisungen
Die Beispiele in diesem Abschnitt veranschaulichen die Regeln zur Schachtelung von Anweisungunge.
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 barrier-Anweisungen
Die Regeln für Anweisungsbindungen verlangen, dass eine barrier-Anweisung an die am nächsten gelegenen einschließende parallel-Anweisung gebunden wird. Weitere Informationen zur Anweisungsbindung finden Sie im Abschnitt 2.8.
Im folgenden Beispiel ist der Aufruf von main an sub2 konform, da die barrier-Anweisung (in sub3) an den parallelen Bereich in sub2 gebunden wird. Der Aufruf von main an sub1 konform, da die barrier-Anweisung an den parallelen Bereich in der Subroutine sub2 gebunden wird. 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-Anweisung
Es ist möglich, viele sortierte Abschnitte mit einer for-Anweisung zu haben, die mit der ordered-Klausel angegeben wird. 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 eine for-Anweisung mit mehr als einem sortierten 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. In diesem Fall kann die copyprivate-Klausel verwendet werden, um Zugriff auf freigegebene Lock-Objekte bereitzustellen, 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 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 Arbeitsteilungskonstrukten innerhalb eines critical-Konstrukts
Im folgenden Beispiel wird die Verwendung eines Arbeitsfreigabekonstrukts innerhalb eines critical-Konstrukts veranschaulicht. Dieses Beispiel ist konform, da das Arbeitsfreigabekonstrukt 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 () {}