Как сделать поток

Добавил пользователь Алексей Ф.
Обновлено: 10.09.2024

Есть два способа создания потоков в Java: унаследоваться от класса Thread или реализовать интерфейс Runnable.

Создание потока через наследование

public class ThinkerThread extends Thread <

@Override
public void run() try TimeUnit. SECONDS .sleep( 3 ); // аналогично Thread.sleep(3000L);
System.out.println( "Второй поток завершён" );
> catch (InterruptedException e) Thread.currentThread().interrupt();
>
>

public static void main(String[] args) new ThinkerThread().start();
System.out.println( "Основной поток завершён" );
>
>

Метод TimeUnit.SECONDS.sleep() полностью эквивалентен стандартному Thread.sleep(), однако в первом случае мы явно указываем единицы измерения времени, что делает код более читаемым. Я рекомендую использовать именно такую форму записи.

Поскольку метод sleep() относится к низкоуровневым, он может кидать InterruptedException, которое свидетельствует о том, что поток был прерван. Это исключение является единственным признаком того, что поток был принудительно остановлен. И чтобы не терять эту информацию в стеке вызовов, мы выставляем флаг interrupted при помощи соответствующего метода Thread.currentThread(). Такой способ обработки InterruptedException является правильным и именно его нужно использовать в подобных случаях.

Создание потока через интерфейс

Теперь создадим поток с помощью интерфейса Runnable.

public class ThinkerRunnable implements Runnable <

@Override
public void run() try TimeUnit. SECONDS .sleep( 3 );
System.out.println( "Второй поток завершён" );
> catch (InterruptedException e) Thread.currentThread().interrupt();
>
>

public static void main(String[] args) new Thread( new ThinkerRunnable()).start();
System.out.println( "Основной поток завершён" );
>
>

Интерфейс Runnable требует определить единственный метод run(). Для запуска потока требуется создать объект Thread, передав ему в конструктор экземпляр нашего класса. Затем у этого объекта Thread вызываем метод start(). Данный вариант является более предпочтительным, если наш класс уже наследуется от какого-то другого базового класса.

Потоки-демоны

В Java процесс завершается тогда, когда завершается последний его поток. Даже если метод main() уже завершился, но еще выполняются порожденные им потоки, система будет ждать их завершения.

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

Сделать поток демоном очень просто - достаточно установить соответствующий флаг с помощью метода setDaemon().

public class DaemonThread extends Thread <

@Override
public void run() try TimeUnit. SECONDS .sleep( 3 );
System.out.println( "Поток-демон завершён" );
> catch (InterruptedException e) Thread.currentThread().interrupt();
>
>

public static void main(String[] args) Thread thread = new DaemonThread();
thread.setDaemon( true );
thread.start();
System.out.println( "Основной поток завершён" );
>
>

Несмотря на то, что поток-демон останавливается на три секунды, мы эти три секунды ждать не будем и программа завершит свою работу. При это исключение InterruptedException не возникает.

Как остановить поток

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

public class ThinkerThread extends Thread <

@Override
public void run() try TimeUnit. SECONDS .sleep( 3 );
System.out.println( "Второй поток завершён" );
> catch (InterruptedException e) Thread.currentThread().interrupt();
>
>

public static void main(String[] args) Thread thread = new ThinkerThread();
thread.start();
System.out.println( "Основной поток завершён" );
thread.interrupt();
>
>

Но вызов interrupt() следует толковать не как принудительную остановку, а как вежливый запрос. Немного изменим пример, чтобы показать, как должна обрабатываться остановка. Для имитации тяжёлой задачи сделаем цикл в котором будем вычислять тригонометрическую функцию.

Требуется создать поток (thread) для выполнения некоторой задачи, в то время как главный поток продолжает свою работу.

Создайте объект класса thread и передайте ему функтор, который выполняет данную работу. Создание объекта потока приведет к инстанцированию потока операционной системы, который начинает выполнять оператор operator() с вашим функтором (или начинает выполнять функцию, переданную с помощью указателя). Пример 12.1 показывает, как это делается.

Пример 12.1. Создание потока

// Что-нибудь работающее долго.

boost::thread myThread(threadFun); // Создать поток, запускающий

boost.:thread::yield(); // Уступить порожденному потоку квант времени.

// чтобы он мог выполнить какую-то работу.

// Выполнить какую-нибудь другую работу

myThread join(); // Текущий поток (т.е поток функции main) прежде.

// чем завершиться, будет ждать окончания myThread

Теперь перейдем непосредственно к рассмотрению программного кода в примере. Конструктор thread принимает функтор (или указатель функции), имеющий два аргумента и возвращающий void. Рассмотрим следующую строку из примера 12.1.

Она создает в стеке объект myThread, являющийся новым потоком операционной системы, который начинает выполнять функцию threadFun. В этот момент программный код функции threadFun и код функции main (по крайней мере, теоретически) выполняются параллельно. Конечно, на самом деле они могут выполняться не параллельно, поскольку ваша машина может иметь только один процессор, и в этом случае параллельная работа невозможна (благодаря недавно разработанным архитектурам процессоров это утверждение не совсем точное, но в настоящий момент я не буду принимать в расчет двухъядерные процессоры и т.п.). Если у вас только один процессор, то операционная система предоставит каждому созданному вами потоку квант времени в состоянии выполнения, перед тем как приостановить его работу. Так как эти кванты времени могут иметь различную величину, никогда нельзя с уверенностью сказать, какой из потоков раньше достигнет определенной точки. Именно в этой особенности многопоточного программирования заключается его сложность: состояние многопоточной программы недетерминировано. При выполнении несколько раз одной и той же многопоточной программы можно получить различные результаты. Темой рецепта 12.2 является координация ресурсов, используемых несколькими потоками.

После создания потока myThread поток main продолжает свою работу, по крайней мере на мгновение, пока не достигнет следующей строки.

Это переводит текущий поток (в данном случае поток main) в неактивное состояние, что означает переключение операционной системы на другой поток или процесс, используя некоторую политику, которая зависит от операционной системы. С помощью функции yield операционная система уведомляется о том, что текущий поток хочет уступить оставшуюся часть кванта времени. В это время новый поток выполняет threadFun. После завершения threadFun дочерний поток исчезает. Следует отметить, что объект thread не уничтожается, потому что он является объектом С++, который по-прежнему находится в области видимости. Эта особенность играет важную роль.

Объект потока — это некий объект, существующий в динамической памяти или в стеке и работающий подобно любому другому объекту С++. Когда программный код выходит из области видимости потока, все находящиеся в стеке объекты потока уничтожаются, или, с другой стороны, когда вызывающая программа выполняет оператор delete для thread*, исчезает соответствующий объект thread, который находится в динамической памяти. Но объекты thread выступают просто как прокси относительно реальных потоков операционной системы, и когда они уничтожаются, потоки операционной системы не обязательно исчезают. Они просто отсоединяются, что означает невозможность их подключения в будущем. Это не так уж плохо.

Поэтому предусмотрена функция-член join. Как показано в примере 12.1, вы можете вызвать join, чтобы дождаться завершения работы дочернего потока, join — это вежливый способ уведомления потока, что вы собираетесь ждать завершения его работы.

Поток, вызвавший функцию join, переходит в состояние ожидания, пока не закончит свою работу другой поток, представленный объектом myThread. Если он никогда не завершится, то никогда не завершится и join. Применение join — наилучший способ ожидания завершения работы дочернего потока.

Возможно, вы заметили, что, если передать что-либо осмысленное функции threadFun, но закомментировать join, поток не завершит свою работу. Вы можете убедиться в этом, выполняя в threadFun цикл или какую-нибудь продолжительную операцию. Это объясняется тем, что операционная система уничтожает процесс вместе со всеми его дочерними процессами независимо от того, закончили или нет они свою работу. Без вызова join функция main не будет ждать окончания работы своих дочерних потоков: она завершается, и поток операционной системы уничтожается.

Если требуется создать несколько потоков, рассмотрите возможность их группирования в объект thread_group. Объект thread_group может управлять объектами двумя способами. Во-первых, вы можете вызвать add_thread с указателем на объект thread, и этот объект будет добавлен в группу. Ниже приводится пример.

boost::thread* p = new boost::thread(threadFun);

// выполнить какие-нибудь действия.

При вызове деструктора grp он удалит оператором delete каждый указатель потока, который был добавлен в add_thread. По этой причине вы можете добавлять в thread_group только указатели объектов потоков, размещённых в динамической памяти. Удаляйте поток путем вызова remove_thread с передачей адреса объекта потока (remove_thread находит в группе соответствующий объект потока, сравнивая значения указателей, а не сами объекты). remove_thread удалит указатель, ссылающийся на этот поток группы, но вам придется все же удалить сам поток с помощью оператора delete.

Кроме того, вы можете добавить поток в группу, не создавая его непосредственно, а используя для этого вызов функции create_thread, которая (подобно объекту потока) принимает функтор в качестве аргумента и начинает его выполнение в новом потоке операционной системы. Например, для порождения двух потоков и добавления их в группу сделайте следующее.

grp.create_thread(threadFun); // Теперь группа grp содержит два потока

grp.join_all(); // Подождать завершения всех потоков

При добавлении потоков в группу при помощи create_thread или add_thread вы можете вызвать join_all для ожидания завершения работы всех потоков группы. Вызов join_all равносилен вызову join для каждого потока группы: join_all возвращает управление после завершения работы всех потоков группы.

Создание объекта потока позволяет начать выполнение отдельного потока. Однако с помощью средств библиотеки Boost Threads это делается обманчиво легко, поэтому необходимо тщательно обдумывать проект. Прочтите остальные рецепты настоящей главы, где даются дополнительные предостережения относительно применения потоков.

Ожидание завершения потока

Ожидание завершения потока Поток может дожидаться завершения выполнения другого потока точно так же, как потоки могут дожидаться завершения процесса, что обсуждалось в главе 6. В этом случае при вызове функций ожидания (WaitForSingleObject и WaitForMultipleObjects) вместо дескрипторов

Создание объектов потока:

Запуск потока

Запуск потока Теперь, когда мы знаем, как запустить другой процесс, давайте рассмотрим, как осуществить запуск другого потока.Любой поток может создать другой поток в том же самом процессе; на это не налагается никаких ограничений (за исключением объема памяти, конечно!)

Создание нового потока

Создание нового потока Создание нового потока в программном коде осуществляет вызов:int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg);где thread — NULL или указатель переменной типа pthread_t, значение которой будет загружено идентификатором созданного потока после

Атрибуты потока

Атрибуты потока В коде реальных приложений очень часто можно видеть простейшую форму вызова, порождающего новый поток, в следующем виде:pthread_create(NULL, NULL, &thread_func, NULL);И для многих целей такого вызова достаточно, так как созданный поток будет обладать свойствами,

Данные потока

Данные потока В реальном коде часто возникает ситуация, когда одновременно исполняются несколько экземпляров потоков, использующих один и тот же код (при создании потоков указывается одна и та же функция потока). При этом некоторые данные (например, статические объекты,

Завершение потока

Зона потока

10.5. Создание класса, считываемого из потока

10.5. Создание класса, считываемого из потока ПроблемаВ поток записан объект некоторого класса и теперь требуется считать эти данные из потока и использовать их для инициализации объекта того же самого класса.РешениеИспользуйте operator>> для чтения данных из потока в ваш

12.1. Создание потока

12.1. Создание потока ПроблемаТребуется создать поток (thread) для выполнения некоторой задачи, в то время как главный поток продолжает свою работу.РешениеСоздайте объект класса thread и передайте ему функтор, который выполняет данную работу. Создание объекта потока приведет к

Синхронизация вызывающего потока

4.2. Отмена потока

4.2. Отмена потока Обычно поток завершается при выходе из потоковой функции или вследствие вызова функции pthread_exit(). Но существует возможность запросить из одного потока уничтожение другого. Это называется отменой, или принудительным завершением, потока.Чтобы отменить

20.7. Состояния потока

20.7. Состояния потока Пользователей библиотеки iostream, разумеется, интересует, находится ли поток в ошибочном состоянии. Например, если мы пишемint ival;cin ival;и вводим слово "Borges", то cin переводится в состояние ошибки после неудачной попытки присвоить строковый литерал целому

8.4.2 Состояния Потока

8.4.2 Состояния Потока Каждый поток (istream или ostream) имеет ассоциированное с ним состояние, и обработка ошибок и нестандартных условий осуществляется с помощью соответствующей установки и проверки этого состояния.Поток может находиться в одном из следующих состояний:enum

Модуль threading в Python используется для реализации многопоточности в программах. В этом материале разберемся с Thread и разными функциями этого модуля.

Что такое поток?

В информатике поток — это минимальная единица работы, запланированная для выполнения операционной системой.

О потоках нужно знать следующее:

  • Они существуют внутри процесса;
  • В одном процессе может быть несколько потоков;
  • Потоки в одном процессе разделяют состояние и память родительского процесса.

Модуль threading в Python можно представить таким простым примером:

После запуска скрипта вывод будет следующий:

У вас он может отличаться, потому что у параллельных потоков нет определенного порядка.

Функции threading в Python

Возьмем программу из первого примера и воспользуемся ею для демонстрации разных функций модуля.

threading.active_count()

Эта функция возвращает количество исполняемых на текущий момент потоков. Изменим последнюю программу, чтобы она выглядела вот так:

Теперь в выводе будет показываться количество активных на текущий момент потоков:

Также обратите внимание, что после запуска всех потоков счетчик показывает число 11, а не 10. Причина в том, что основной поток также учитывается наравне с 10 остальными.

threading.current_thread()

Эта функция возвращает исполняемый прямо сейчас поток. С ее помощью можно выполнять определенные действия с ним. Поменяем все тот же скрипт:

Теперь вывод будет таким:

threading.main_thread()

Эта функция возвращает основной поток программы. Именно из него создаются новые потоки.

threading.enumerate()

Эта функция возвращает список всех активных потоков. Пользоваться ею проще простого:

Имя потока MainThread.
Имя потока SockThread.

Сейчас работает только 2 потока, поэтому в выводе есть только они.

threading.Timer()

Эта функция модуля threading используется для создания нового потока и указания времени, через которое он должен запуститься. После запуска поток вызывает определенную функцию. Разберем на примере:

Вывод через 5 секунд!

Многопоточность в Python

В этом материале были разобраны некоторые функции модуля threading . Они предоставляют удобные методы для управления потоками в многопоточной среде.

Обучение с трудоустройством

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



Это страницы, которые открываются мгновенно на мобильных устройствах при переходе из поисковых систем. У потоков автоматически генерируется RSS лента. Ее можно подключить в Яндекс Вебмастере, для турбо-страниц и на Яндекс Дзене. Ссылку на RSS ленту можно скопировать в настройках потока.

Читайте также: