Потоки в boost

Boost — невероятно мощное собрание библиотек для C++, в котором реализовано множество классов для работы с потоками, датой и временем, интерфейсами файловой системы, сети и множество других.

И что особенно радует — использование boost не означает значимого снижения производительности. Даже хорошо реализованный алгоритм будет чаще всего лишь немного быстрее своего аналога из boost, а что уж говорить про "костыли"... Словом, почти всегда лучше использовать возможности, предлагаемые бустом, чем изобретать велосипед.

И одна из этих возможностей, о которой я хочу рассказать — создание потоков и управление ими.

boost::thread позволяет создавать обычные для вашей системы потоки. Например в POSIX-системе boost::thread использует PThreads API, а в Windows использует функцию _beginthreadex (если кто не в курсе — это вроде как "аналог" CreateThread). Таким образом boost::thread абстрагирует вас от конкретной платформы и позволяет писать быстрый и переносимый код, который можно собрать на любой системе, для которой есть boost.

Для понимания этой статьи вам понадобится иметь по крайней мере небольшое представление о том что такое потоки, как они создаются и работают в вашей операционной системе, т.к. здесь будут обсуждаться лишь вопросы, связанные конкретно с использованием boost.

Заранее скачайте архив с исходными кодами в конце статьи!

Также вам потребуется установить boost. Скачать его можно на официальном сайте boost. Если вы работаете с Visual Studio, то в качестве помощи к мануалу из пакета предлагаю использовать это видео:

Если же вы работаете с Embarcadero RAD Studio, используйте мастер установки, чтобы установить Boost Libraries. Подробно останавливаться на этом моменте не буду, и в дальнейшем предполагаю что вы уже загрузили и установили boost, а значит можно начинать.

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

Способ I: Функция, выполняющая в потоке
Самый простой вариант создать поток с помощью boost — передать в качестве параметра конструктору boost::thread функцию, которая будет выполняться в созданном потоке.

// Эта функция будет передана в конструктор и станет выполняться в потоке
void ThreadFunction()	
{
    printf("Thread: Hello!\n");
    boost::posix_time::seconds SleepTime(2);
    boost::this_thread::sleep(SleepTime);	//	Ждем 2 секунды (подробее см. ниже)
    printf("Thread: Bye Bye!");
}
 
int main()
{
    boost::thread Thread(ThreadFunction);	//	Создаем поток, в котором будет выполняться ThreadFunction()
    Thread.join();				//	Используем join, чтобы дождать его завершения (подробее см. ниже)
    return 0;
}

Запустите программу и вы увидите:

Thread: Hello!
Thread: Bye Bye!

Общая схема выполнения программы такова:

Приведенный выше код создал поток и запустил его, просто передав в конструктор boost::thread имя функции ThreadFunction.
После этого, был вызван метод join(), который приостановил выполнение main() на время работы потока. Как только поток завершился, main() продолжил работу. Проще некуда, правда?

Есть лишь несколько пояснений к данному коду:

boost::this_thread
boost::this_thread — это, как несложно догадаться из названия, класс который позволяет нам сослаться на текущий поток.

boost::this_thread::sleep
Чтобы уменьшить количество кода, в этом и всех других примерах вместо выполнения какой-то реальной работы потоком используется простой вызов sleep. Для этого сначала создается объект с данными о продолжительности периода в 2 секунды (boost::posix_time::seconds workTime(2);), а потом он передается как параметр в метод boost::this_thread::sleep (boost::this_thread::sleep(workTime); )

Метод join
Для того, чтобы дождаться выполнения потока используется метод join(). Вроде как это обертка вокруг WaitForSingleObject для Windows. Так или иначе, этот метод задерживает передачу управления до тех пор, пока поток не завершится (абсолютно неважно успешно или нет).

Способ II: Функция с параметрами
В первом способе создания потока с помощью boost все хорошо, но что делать если требуется передавать в функцию параметры? Один из конструкторов boost::thread позволяет это сделать, причем очень легко. Просто добавляйте параметры в конструктор объекта, и они будут переданы в вызываемую функцию.

Допустим, что функция имеет следующий вид:

void ThreadFunction(const char* Message, float TimeOut);

Тогда создание потока с передачей в него параметров сводится к следующему:
boost::thread Thread(ThreadFunction, "Boost is cool", 2.5e3);

Способ III: Функтор
Функтор — это класс, в котором перегружен оператор (), и который благодаря этому может вести себя и как объект и как функция одновременно. Метод, определенный при перегрузке () срабатывает при использовании функтора как функции.

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

class cCircleToDiameter
{
    public:
        cCircleToDiameter(float pCircleLen) : CircleLen(pCircleLen) {}	//	Длина окружности передается в конструктор
 
        void operator() ()	//	Вычисление будет произведено при вызове ()
        {
            float Diameter = CircleLen / 3.1415926535;
            printf("Diameter = %f\n", Diameter);
        }
 
    private:
        float CircleLen;
};

А вот так его можно использовать:

int main(int argc, char* argv[])
{
    cCircleToDiameter CircleToDiameter(16);
    boost::thread Thread(CircleToDiameter);
    Thread.join();
    return 0;
}

Здесь мы в функции main() создаем объект, передавая ему параметр — длину окружности. Потом передаем созданный объект в конструктор boost:::thread, который сам вызывает перегруженный оператор () функтора.

Такой способ позволяет нам создавать поток, однако при этом иметь объект со всеми его данными и методами. Это может быть хорошим способом создания удобного враппера.

Важно понимать, что конструктор boost::thread берет функтор-параметр по значению, что означает создание копии объекта.
Не стоит забывать об этом при реализации функтора, чтобы избежать возможных проблем.

Способ IV: Объектный
Часто удобно реализовать объект с функцией, которая будет выполняться в отдельном потоке.

В таком случае в конструктор boost::thread необходимо передать имя функции, используя имя класса, ссылку на экземпляр класса и при необходимости параметры функции.

cClass Class;
boost::thread Thread(&cClass::ClassFunction, &Class, 3);	//	ClassFunction() будет выполняться в потоке

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

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

В последнем примере поток создается внутри объекта, а сам объект предоставляет методы для управления потоком.

Помятуя об инкапсуляции, сделаем ссылку приватной:

private:
    boost::thread Thread;

А создание потока возложим на метод Start(), а не на конструктор:

void Start(int Seconds)
{
    Thread = boost::thread(&cClass::ClassFunction, this, Seconds);
}

Также метод join():

void join()
{
    Thread.join();
}

А значит функция main будет выглядеть так:

int main(int argc, char* argv[])
{
    cClass Class;
    Class.Start(2);
    Class.join();
    return 0;
}

Вот и все! Мы рассмотрели множество способов создать поток с помощью boost - от простой функции, до объекта, управляющего потоком. Возникающие вопросы задавайте в комментариях, буду по возможности отвечать.
По мотивам antonym.org/2009/05/threading-with-boost---part-i-creating-threads.html
 

ПриложениеРазмер
Исходные коды всех примеров из статьи1.99 кб
  • Категории:

Комментарии

Рассмотрите типичные ошибки.

Не работает, и, как новичок, я нихрена не могу разобраться в этих потоках.
Boost 1.51.0

Конкретно не работает закидывание метода класса в поток, т.е. 4 и 5, уже замучился искать инструкции гуглом :)

А в чем именно проблема?
Компилятор ругается или поток не создается?

Второй способ пригодился. =)
Всё просто и понятно, спасибо!

Все работает) А есть ли способ проверить, что поток точно запущен?
Спасибо.

Даже I не компилится.
отсутствуют экземпляры конструктора boost::thread::thread соответствующие типу void.

не забывайте про
#include

все работает спс

Добавить комментарий

Адрес показан не будет
CAPTCHA
Антибот
Как оформить комментарий?