Многопоточность С++
Многопоточность — это особая форма многозадачности, а многозадачность — это функция, которая позволяет вашему компьютеру одновременно запускать две или более программы. Как правило, существует два типа многозадачности:на основе процессов и на основе потоков.
Многозадачность на основе процессов обеспечивает одновременное выполнение программ. Многозадачность на основе потоков связана с одновременным выполнением частей одной и той же программы.
Многопоточная программа состоит из двух или более частей, которые могут выполняться одновременно. Каждая часть такой программы называется потоком, и каждый поток определяет отдельный путь выполнения.
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 создает новый поток и делает его исполняемым. Эту подпрограмму можно вызывать любое количество раз из любого места вашего кода. Вот описание параметров —
Старший № | Параметр и описание |
---|---|
1 | <тд>|
2 | <тд>|
3 | <тд>|
4 | <тд>
Максимальное количество потоков, которое может быть создано процессом, зависит от реализации. После создания потоки становятся одноранговыми и могут создавать другие потоки. Между потоками нет подразумеваемой иерархии или зависимости.
Завершение потоков
Существует следующая процедура, которую мы используем для завершения потока 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); }
Когда приведенный выше код скомпилирован и выполнен, он дает следующий результат —
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.
Язык C