Đa luồng C ++

Đa luồng là một dạng đa nhiệm chuyên biệt và đa nhiệm là tính năng cho phép máy tính của bạn chạy đồng thời hai hoặc nhiều chương trình. Nói chung, có hai loại đa nhiệm: dựa trên quy trình và dựa trên luồng.

Đa nhiệm dựa trên quy trình xử lý việc thực thi đồng thời các chương trình. Đa nhiệm dựa trên luồng xử lý việc thực thi đồng thời các phần của cùng một chương trình.

Một chương trình đa luồng chứa hai hoặc nhiều phần có thể chạy đồng thời. Mỗi phần của chương trình như vậy được gọi là một luồng và mỗi luồng xác định một đường dẫn thực thi riêng.

C ++ không chứa bất kỳ hỗ trợ tích hợp nào cho các ứng dụng đa luồng. Thay vào đó, nó hoàn toàn dựa vào hệ điều hành để cung cấp tính năng này.

Hướng dẫn này giả định rằng bạn đang làm việc trên Hệ điều hành Linux và chúng ta sẽ viết chương trình C ++ đa luồng bằng POSIX. POSIX Threads, hoặc Pthreads cung cấp API có sẵn trên nhiều hệ thống POSIX giống Unix như FreeBSD, NetBSD, GNU / Linux, Mac OS X và Solaris.

Tạo chủ đề

Quy trình sau đây được sử dụng để tạo một chuỗi POSIX:

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

Đây, pthread_createtạo một luồng mới và làm cho nó có thể thực thi được. Quy trình này có thể được gọi bất kỳ số lần nào từ bất kỳ đâu trong mã của bạn. Đây là mô tả của các tham số -

Sr.No Mô tả về Thông Số
1

thread

Một mã định danh không rõ ràng, duy nhất cho luồng mới được chương trình con trả về.

2

attr

Một đối tượng thuộc tính không rõ ràng có thể được sử dụng để đặt thuộc tính luồng. Bạn có thể chỉ định một đối tượng thuộc tính luồng hoặc NULL cho các giá trị mặc định.

3

start_routine

Quy trình C ++ mà luồng sẽ thực thi khi nó được tạo.

4

arg

Một đối số duy nhất có thể được chuyển đến start_routine. Nó phải được truyền bằng tham chiếu dưới dạng một con trỏ kiểu void. NULL có thể được sử dụng nếu không có đối số nào được truyền.

Số luồng tối đa có thể được tạo bởi một quy trình phụ thuộc vào việc triển khai. Sau khi được tạo, các luồng là đồng đẳng và có thể tạo ra các luồng khác. Không có hệ thống phân cấp hoặc sự phụ thuộc ngụ ý giữa các luồng.

Kết thúc chủ đề

Có một quy trình sau đây mà chúng tôi sử dụng để kết thúc một chuỗi POSIX:

#include <pthread.h>
pthread_exit (status)

Đây pthread_exitđược sử dụng để thoát khỏi một chủ đề một cách rõ ràng. Thông thường, quy trình pthread_exit () được gọi sau khi một luồng đã hoàn thành công việc của nó và không còn bắt buộc phải tồn tại.

Nếu main () kết thúc trước khi luồng mà nó đã tạo và thoát bằng pthread_exit (), các luồng khác sẽ tiếp tục thực thi. Nếu không, chúng sẽ tự động kết thúc khi main () kết thúc.

Example

Mã ví dụ đơn giản này tạo 5 luồng với quy trình pthread_create (). Mỗi chuỗi in một "Hello World!" và sau đó kết thúc bằng lệnh gọi đến pthread_exit ().

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

Biên dịch chương trình sau bằng thư viện -lpthread như sau:

$gcc test.cpp -lpthread

Bây giờ, hãy thực thi chương trình của bạn để đưa ra kết quả sau:

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

Chuyển đối số thành chủ đề

Ví dụ này cho thấy cách truyền nhiều đối số qua một cấu trúc. Bạn có thể chuyển bất kỳ kiểu dữ liệu nào trong một chuỗi gọi lại vì nó trỏ đến void như được giải thích trong ví dụ sau:

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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

Tham gia và tách chuỗi

Có hai quy trình sau đây mà chúng ta có thể sử dụng để tham gia hoặc tách các chuỗi -

pthread_join (threadid, status) 
pthread_detach (threadid)

Chương trình con pthread_join () chặn luồng đang gọi cho đến khi luồng 'threadid' được chỉ định kết thúc. Khi một luồng được tạo, một trong các thuộc tính của nó xác định xem nó có thể nối hay tách rời. Chỉ các chủ đề được tạo dưới dạng có thể nối mới có thể được tham gia. Nếu một chuỗi được tạo ra dưới dạng tách rời, nó không bao giờ có thể được tham gia.

Ví dụ này trình bày cách chờ hoàn thành chuỗi bằng cách sử dụng quy trình tham gia Pthread.

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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.