C ++ Multithreading

Multithreading ist eine spezielle Form des Multitasking und ein Multitasking ist die Funktion, mit der Ihr Computer zwei oder mehr Programme gleichzeitig ausführen kann. Im Allgemeinen gibt es zwei Arten von Multitasking: prozessbasiert und threadbasiert.

Prozessbasiertes Multitasking übernimmt die gleichzeitige Ausführung von Programmen. Thread-basiertes Multitasking befasst sich mit der gleichzeitigen Ausführung von Teilen desselben Programms.

Ein Multithread-Programm enthält zwei oder mehr Teile, die gleichzeitig ausgeführt werden können. Jeder Teil eines solchen Programms wird als Thread bezeichnet, und jeder Thread definiert einen separaten Ausführungspfad.

C ++ enthält keine integrierte Unterstützung für Multithread-Anwendungen. Stattdessen ist es vollständig auf das Betriebssystem angewiesen, um diese Funktion bereitzustellen.

In diesem Tutorial wird davon ausgegangen, dass Sie unter Linux arbeiten und wir ein Multithread-C ++ - Programm mit POSIX schreiben werden. POSIX Threads oder Pthreads bietet APIs, die auf vielen Unix-ähnlichen POSIX-Systemen wie FreeBSD, NetBSD, GNU / Linux, Mac OS X und Solaris verfügbar sind.

Threads erstellen

Die folgende Routine wird verwendet, um einen POSIX-Thread zu erstellen:

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

Hier, pthread_createErstellt einen neuen Thread und macht ihn ausführbar. Diese Routine kann beliebig oft von überall in Ihrem Code aufgerufen werden. Hier ist die Beschreibung der Parameter -

Sr.Nr. Parameter & Beschreibung
1

thread

Eine undurchsichtige, eindeutige Kennung für den neuen Thread, der von der Unterroutine zurückgegeben wird.

2

attr

Ein undurchsichtiges Attributobjekt, mit dem Thread-Attribute festgelegt werden können. Sie können ein Threadattributobjekt oder NULL für die Standardwerte angeben.

3

start_routine

Die C ++ - Routine, die der Thread nach seiner Erstellung ausführt.

4

arg

Ein einzelnes Argument, das an start_routine übergeben werden kann. Es muss als Zeiger vom Typ void referenziert werden. NULL kann verwendet werden, wenn kein Argument übergeben werden soll.

Die maximale Anzahl von Threads, die von einem Prozess erstellt werden können, hängt von der Implementierung ab. Nach dem Erstellen sind Threads Peers und können andere Threads erstellen. Es gibt keine implizite Hierarchie oder Abhängigkeit zwischen Threads.

Threads beenden

Es gibt folgende Routine, mit der wir einen POSIX-Thread beenden:

#include <pthread.h>
pthread_exit (status)

Hier pthread_exitwird verwendet, um einen Thread explizit zu beenden. In der Regel wird die Routine pthread_exit () aufgerufen, nachdem ein Thread seine Arbeit abgeschlossen hat und nicht mehr vorhanden sein muss.

Wenn main () vor den von ihm erstellten Threads beendet wird und mit pthread_exit () beendet wird, werden die anderen Threads weiterhin ausgeführt. Andernfalls werden sie automatisch beendet, wenn main () beendet ist.

Example

Dieser einfache Beispielcode erstellt 5 Threads mit der Routine pthread_create (). Jeder Thread druckt eine "Hallo Welt!" Nachricht und wird dann mit einem Aufruf von pthread_exit () beendet.

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

void *PrintHello(void *threadid) {
   long tid;
   tid = (long)threadid;
   cout << "Hello World! Thread ID, " << tid << endl;
   pthread_exit(NULL);
}

int main () {
   pthread_t threads[NUM_THREADS];
   int rc;
   int i;
   
   for( i = 0; i < NUM_THREADS; i++ ) {
      cout << "main() : creating thread, " << i << endl;
      rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
      
      if (rc) {
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

Kompilieren Sie das folgende Programm mit der Bibliothek -lpthread wie folgt:

$gcc test.cpp -lpthread

Führen Sie nun Ihr Programm aus, das die folgende Ausgabe liefert:

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Hello World! Thread ID, 0
Hello World! Thread ID, 1
Hello World! Thread ID, 2
Hello World! Thread ID, 3
Hello World! Thread ID, 4

Argumente an Threads übergeben

Dieses Beispiel zeigt, wie mehrere Argumente über eine Struktur übergeben werden. Sie können einen beliebigen Datentyp in einem Thread-Rückruf übergeben, da dieser auf void zeigt, wie im folgenden Beispiel erläutert.

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

struct thread_data {
   int  thread_id;
   char *message;
};

void *PrintHello(void *threadarg) {
   struct thread_data *my_data;
   my_data = (struct thread_data *) threadarg;

   cout << "Thread ID : " << my_data->thread_id ;
   cout << " Message : " << my_data->message << endl;

   pthread_exit(NULL);
}

int main () {
   pthread_t threads[NUM_THREADS];
   struct thread_data td[NUM_THREADS];
   int rc;
   int i;

   for( i = 0; i < NUM_THREADS; i++ ) {
      cout <<"main() : creating thread, " << i << endl;
      td[i].thread_id = i;
      td[i].message = "This is message";
      rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
      
      if (rc) {
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message

Threads verbinden und trennen

Es gibt zwei Routinen, mit denen wir Threads verbinden oder trennen können:

pthread_join (threadid, status) 
pthread_detach (threadid)

Die Unterroutine pthread_join () blockiert den aufrufenden Thread, bis der angegebene Thread 'threadid' beendet wird. Wenn ein Thread erstellt wird, definiert eines seiner Attribute, ob er verbunden oder getrennt werden kann. Es können nur Threads verbunden werden, die als verbindbar erstellt wurden. Wenn ein Thread als getrennt erstellt wird, kann er niemals verbunden werden.

Dieses Beispiel zeigt, wie Sie mithilfe der Pthread-Join-Routine auf Thread-Vervollständigungen warten.

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

using namespace std;

#define NUM_THREADS 5

void *wait(void *t) {
   int i;
   long tid;

   tid = (long)t;

   sleep(1);
   cout << "Sleeping in thread " << endl;
   cout << "Thread with id : " << tid << "  ...exiting " << endl;
   pthread_exit(NULL);
}

int main () {
   int rc;
   int i;
   pthread_t threads[NUM_THREADS];
   pthread_attr_t attr;
   void *status;

   // Initialize and set thread joinable
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

   for( i = 0; i < NUM_THREADS; i++ ) {
      cout << "main() : creating thread, " << i << endl;
      rc = pthread_create(&threads[i], &attr, wait, (void *)i );
      if (rc) {
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }

   // free attribute and wait for the other threads
   pthread_attr_destroy(&attr);
   for( i = 0; i < NUM_THREADS; i++ ) {
      rc = pthread_join(threads[i], &status);
      if (rc) {
         cout << "Error:unable to join," << rc << endl;
         exit(-1);
      }
      cout << "Main: completed thread id :" << i ;
      cout << "  exiting with status :" << status << endl;
   }

   cout << "Main: program exiting." << endl;
   pthread_exit(NULL);
}

Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 0 .... exiting
Sleeping in thread
Thread with id : 1 .... exiting
Sleeping in thread
Thread with id : 2 .... exiting
Sleeping in thread
Thread with id : 3 .... exiting
Sleeping in thread
Thread with id : 4 .... exiting
Main: completed thread id :0  exiting with status :0
Main: completed thread id :1  exiting with status :0
Main: completed thread id :2  exiting with status :0
Main: completed thread id :3  exiting with status :0
Main: completed thread id :4  exiting with status :0
Main: program exiting.