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

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

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

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 создает новый поток и делает его исполняемым. Эта процедура может вызываться любое количество раз из любого места вашего кода. Вот описание параметров:

thread Непрозрачный уникальный идентификатор для нового потока, возвращаемого подпрограммой.
attr Объект непрозрачного атрибута, который может использоваться для установки атрибутов потока. Вы можете указать объект атрибутов потока или 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 () блокирует вызывающий поток до тех пор, пока указанный поток потоков не завершится. Когда поток создается, один из его атрибутов определяет, является ли он совместимым или отсоединенным. Можно объединить только те потоки, которые созданы как соединяемые. Если поток создается как отсоединенный, он никогда не может быть соединен.

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

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

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.