C ++ Multithreading

Çoklu okuma, özel bir çoklu görev biçimidir ve çoklu görev, bilgisayarınızın aynı anda iki veya daha fazla programı çalıştırmasına izin veren bir özelliktir. Genel olarak, iki tür çoklu görev vardır: işlem tabanlı ve iş parçacığı tabanlı.

Süreç tabanlı çoklu görev, programların eşzamanlı olarak yürütülmesini sağlar. İş parçacığı tabanlı çoklu görev, aynı programın parçalarının eşzamanlı olarak yürütülmesiyle ilgilenir.

Çok iş parçacıklı bir program aynı anda çalışabilen iki veya daha fazla parça içerir. Böyle bir programın her parçası bir evre olarak adlandırılır ve her evre ayrı bir yürütme yolunu tanımlar.

C ++, çok iş parçacıklı uygulamalar için herhangi bir yerleşik destek içermez. Bunun yerine, bu özelliği sağlamak için tamamen işletim sistemine güvenir.

Bu eğitim, Linux işletim sistemi üzerinde çalıştığınızı ve POSIX kullanarak çok iş parçacıklı C ++ programı yazacağımızı varsayar. POSIX Threads veya Pthreads, FreeBSD, NetBSD, GNU / Linux, Mac OS X ve Solaris gibi birçok Unix benzeri POSIX sisteminde bulunan API sağlar.

Konu Oluşturma

Aşağıdaki rutin bir POSIX iş parçacığı oluşturmak için kullanılır -

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

Buraya, pthread_createyeni bir iş parçacığı oluşturur ve çalıştırılabilir hale getirir. Bu rutin, kodunuzun herhangi bir yerinden herhangi bir sayıda çağrılabilir. İşte parametrelerin açıklaması -

Sr.No Parametre ve Açıklama
1

thread

Alt rutin tarafından döndürülen yeni evre için opak, benzersiz bir tanımlayıcı.

2

attr

İş parçacığı niteliklerini ayarlamak için kullanılabilen opak bir nitelik nesnesi. Varsayılan değerler için bir iş parçacığı nitelikleri nesnesi veya NULL belirtebilirsiniz.

3

start_routine

İş parçacığının oluşturulduktan sonra yürüteceği C ++ rutini.

4

arg

Start_routine'e aktarılabilen tek bir argüman. Void türünde bir işaretçi dökümü olarak başvuruya göre aktarılmalıdır. Hiçbir bağımsız değişken geçirilmeyecekse NULL kullanılabilir.

Bir işlem tarafından yaratılabilecek maksimum iş parçacığı sayısı uygulamaya bağlıdır. Bir kez oluşturulduktan sonra, iş parçacıkları eşlerdir ve başka iş parçacıkları oluşturabilir. İş parçacıkları arasında zımni hiyerarşi veya bağımlılık yoktur.

Konuları Sonlandırmak

Bir POSIX iş parçacığını sonlandırmak için kullandığımız aşağıdaki rutin var -

#include <pthread.h>
pthread_exit (status)

Buraya pthread_exitbir iş parçacığından açıkça çıkmak için kullanılır. Tipik olarak, pthread_exit () yordamı, bir iş parçacığı çalışmasını tamamladıktan sonra çağrılır ve artık var olması gerekmez.

Main (), oluşturduğu evrelerden önce biterse ve pthread_exit () ile çıkarsa, diğer evreler çalışmaya devam edecektir. Aksi takdirde, main () bittiğinde otomatik olarak sonlandırılacaktır.

Example

Bu basit örnek kod, pthread_create () rutini ile 5 evre oluşturur. Her iş parçacığı bir "Merhaba Dünya!" mesaj ve sonra pthread_exit () çağrısıyla sona erer.

#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);
}

Aşağıdaki programı -lpthread kitaplığını kullanarak aşağıdaki gibi derleyin -

$gcc test.cpp -lpthread

Şimdi, aşağıdaki çıktıyı veren programınızı çalıştırın -

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

Bağımsız Değişkenleri İş Parçacıklarına Aktarma

Bu örnek, bir yapı aracılığıyla birden çok argümanın nasıl iletileceğini gösterir. Bir iş parçacığı geri aramasında herhangi bir veri türünü iletebilirsiniz çünkü aşağıdaki örnekte açıklandığı gibi void'i işaret eder -

#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);
}

Yukarıdaki kod derlendiğinde ve yürütüldüğünde, aşağıdaki sonucu verir -

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

Konuları Birleştirme ve Ayırma

Konuları birleştirmek veya ayırmak için kullanabileceğimiz aşağıdaki iki rutin vardır:

pthread_join (threadid, status) 
pthread_detach (threadid)

Pthread_join () alt rutini, belirtilen 'threadid' evre sona erene kadar çağıran evreyi bloke eder. Bir iş parçacığı oluşturulduğunda, niteliklerinden biri onun birleştirilebilir mi yoksa ayrılabilir mi olduğunu tanımlar. Yalnızca birleştirilebilir olarak oluşturulan iş parçacıkları birleştirilebilir. Bir iş parçacığı ayrılmış olarak oluşturulursa, asla birleştirilemez.

Bu örnek, Pthread birleştirme rutini kullanılarak iş parçacığı tamamlamalarının nasıl bekleneceğini gösterir.

#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);
}

Yukarıdaki kod derlendiğinde ve yürütüldüğünde, aşağıdaki sonucu verir -

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.