Учебники

C ++ Многопоточность

Многопоточность — это специализированная форма многозадачности, а многозадачность — это функция, которая позволяет вашему компьютеру запускать две или более программ одновременно. В общем, существует два типа многозадачности: на основе процессов и на основе потоков.

Многозадачность на основе процессов обрабатывает параллельное выполнение программ. Многозадачность на основе потоков имеет дело с параллельным выполнением частей одной и той же программы.

Многопоточная программа содержит две или более частей, которые могут работать одновременно. Каждая часть такой программы называется потоком, и каждый поток определяет отдельный путь выполнения.

C ++ не содержит встроенной поддержки многопоточных приложений. Вместо этого он полностью полагается на операционную систему, чтобы обеспечить эту функцию.

В этом руководстве предполагается, что вы работаете в ОС Linux, и мы собираемся написать многопоточную программу на C ++ с использованием POSIX. POSIX Threads, или Pthreads, предоставляет API, который доступен во многих Unix-подобных системах POSIX, таких как FreeBSD, NetBSD, GNU / Linux, Mac OS X и Solaris.

Создание тем

Следующая процедура используется для создания потока POSIX —

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

Здесь pthread_create создает новый поток и делает его исполняемым. Эта процедура может вызываться любое количество раз из любого места в вашем коде. Вот описание параметров —

Sr.No Параметр и описание
1

нить

Непрозрачный уникальный идентификатор для нового потока, возвращаемого подпрограммой.

2

атр

Непрозрачный объект атрибута, который можно использовать для установки атрибутов потока. Вы можете указать объект атрибутов потока или NULL для значений по умолчанию.

3

start_routine

Подпрограмма C ++, которую поток выполнит, как только он будет создан.

4

Arg

Единственный аргумент, который может быть передан start_routine. Он должен быть передан по ссылке как приведение указателя типа void. NULL может использоваться, если аргумент не передается.

нить

Непрозрачный уникальный идентификатор для нового потока, возвращаемого подпрограммой.

атр

Непрозрачный объект атрибута, который можно использовать для установки атрибутов потока. Вы можете указать объект атрибутов потока или NULL для значений по умолчанию.

start_routine

Подпрограмма C ++, которую поток выполнит, как только он будет создан.

Arg

Единственный аргумент, который может быть передан start_routine. Он должен быть передан по ссылке как приведение указателя типа void. NULL может использоваться, если аргумент не передается.

Максимальное количество потоков, которое может быть создано процессом, зависит от реализации. Созданные потоки являются одноранговыми и могут создавать другие потоки. Не существует подразумеваемой иерархии или зависимости между потоками.

Завершающие Темы

Существует следующая процедура, которую мы используем для завершения потока POSIX —

#include <pthread.h>
pthread_exit (status) 

Здесь pthread_exit используется для явного выхода из потока. Как правило, процедура pthread_exit () вызывается после того, как поток завершил свою работу и больше не требуется для существования.

Если main () завершает работу перед созданными потоками и завершается с помощью pthread_exit (), другие потоки продолжат выполняться. В противном случае они будут автоматически завершены, когда main () завершит работу.

пример

Этот простой пример кода создает 5 потоков с помощью процедуры pthread_create (). Каждый поток печатает «Hello World!» сообщение, а затем завершается вызовом 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);
}

Скомпилируйте следующую программу, используя библиотеку -lpthread, следующим образом:

$gcc test.cpp -lpthread

Теперь выполните вашу программу, которая дает следующий вывод —

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

Передача аргументов в потоки

В этом примере показано, как передать несколько аргументов через структуру. Вы можете передать любой тип данных в обратном вызове потока, потому что он указывает на void, как объяснено в следующем примере —

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

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

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

Присоединение и отсоединение потоков

Существуют следующие две процедуры, которые мы можем использовать для присоединения или отсоединения потоков:

pthread_join (threadid, status) 
pthread_detach (threadid) 

Подпрограмма pthread_join () блокирует вызывающий поток до тех пор, пока указанный поток ‘threadid’ не завершится. Когда поток создается, один из его атрибутов определяет, является ли он присоединяемым или отсоединенным. Только потоки, созданные как присоединяемые, могут быть присоединены. Если поток создается как отдельный, он никогда не может быть присоединен.

В этом примере показано, как ожидать завершения потока с помощью процедуры соединения 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);
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —