Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ПИ_ИКТ Программирование по С++ (1 семестр) _Хотов.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
5.83 Mб
Скачать

Локальная память потока

Пример 5 иллюстрирует очень простое применение класса boost::thread_specific_ptr. Создаются два потока, в них инициализируется локальная память потока, а затем в цикле 10 раз значение целого, расположенного по адресу «умного» указателя инкрементируется, а результат выводится в std::cout (который синхронизирован с помощью мьютекса, так как является разделяемым ресурсом). Основной поток ожидает завершения этих двух потоков. Вывод в этом примере ясно показывает, что каждый поток оперирует со своим экземпляром данных, хотя оба они используют один и тот же boost::thread_specific_ptr.

// Пример 5

#include <boost/thread/thread.hpp>

#include <boost/thread/mutex.hpp>

#include <boost/thread/tss.hpp>

#include <iostream>

boost::mutex io_mutex;

boost::thread_specific_ptr<int> ptr;

struct count

{

count(int id) : id(id) { }

void operator()()

{

if (ptr.get() == 0)

ptr.reset(new int(0));

for (int i = 0; i < 10; ++i)

{

(*ptr)++;

boost::mutex::scoped_lock lock(io_mutex);

std::cout << id << ": " << *ptr << std::endl;

}

}

int id;

};

int main(int argc, char* argv[])

{

boost::thread thrd1(count(1));

boost::thread thrd2(count(2));

thrd1.join();

thrd2.join();

return 0;

}

Однократно вызываемые функции

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

Решение проблемы в так называемых «однократно вызываемых функциях» (once routine). Такая функция вызывается в приложении только один раз. Если несколько потоков попытаются ее вызвать одновременно, только один из них получит такую возможность, а в это время все остальные потоки будут ждать, пока выполнение функции не завершится. Чтобы гаранти ровать однократное выполнение, такая функция вызывается косвенно через другую функцию, которой передается указатель на исходную и ссылка на специальный флаг, сигнализирующий о факте вызова функции. Этот флаг инициализируется статически, что гарантирует инициализацию в период компиляции, а не в период выполнения. И таким образом не представляет проблемы для многопоточной инициализации. Библиотека Boost.Threads предоставляет возможность однократного вызова функции посредством boost::call_once, а также определяет тип для флага boost::once_flag и специальную макроподстановку, используемую для статической инициализации флага, BOOST_ONCE_INIT.

Пример 6 показывает очень простой пример использования boost::call_once. Глобальное целое статически инициализируется нулем, а экземпляр boost::once_flag статически инициализируется с помощью BOOST_ONCE_INIT. Основной поток запускает два потока, каждый из которых пытается «инициализировать» глобальное целое, вызывая boost::call_once с указателем на функцию, инкрементирующую целое. Затем основной поток дожидается завершения обоих потоков и выводит конечное значение целого в std::cout. Вывод демонстрирует, что функция действительно была вызвана только однажды, так как значение целого – единица.

// Пример 6

#include <boost/thread/thread.hpp>

#include <boost/thread/once.hpp>

#include <iostream>

int i = 0;

boost::once_flag flag = BOOST_ONCE_INIT;

void init()

{

++i;

}

void thread()

{

boost::call_once(&init, flag);

}

int main(int argc, char* argv[])

{

boost::thread thrd1(&thread);

boost::thread thrd2(&thread);

thrd1.join();

thrd2.join();

std::cout << i << std::endl;

return 0;

}