BOOST::THREADS. Многопоточное программирование 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

BOOST::THREADS. Многопоточное программирование



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

Пример 1 демонстрирует очень простой пример использования класса boost::thread. Создается новый поток, который просто выводит “Hello World” на std::cout, а основной поток ждет его завершения.

// Пример 1

#include <boost/thread/thread.hpp>

#include <iostream>

void hello()

{

std::cout <<

"Hello world, I'm a thread!"

<< std::endl;

}

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

{

boost::thread thrd(&hello);

thrd.join();

return 0;

}

 

 

Мьютексы

В примере 2 показано очень простое использование класса boost::mutex. Создаются два новых потока, каждый из них 10 раз выводит свой id и счетчик цикла, а основной поток дожидается их завершения. Объект std::cout является разделяемым ресурсом, поэтому каждый поток использует глобальный мьютекс, гарантирующий, что только один поток в каждый момент времени пытается осуществлять вывод.

 

// Пример 2

#include <boost/thread/thread.hpp>

#include <boost/thread/mutex.hpp>

#include <iostream>

boost::mutex io_mutex;

struct count

{

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

void operator()()

{

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

{

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

Douglas Schmidt, Michael Stal, Hans Rohnert, and Frank

Buschmann. Pattern-Oriented Software Architecture

Volume 2 — Patterns for Concurrent and Networked

Objects (Wiley, 2000).

(Scoped Lock).

std::cout << id << ": "

<< i << 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;

}

 

Многие пользователи заметят, что передача данных потоку требует ручного кодирования функционального объекта. Хотя этот код и тривиален, писать его всякий раз довольно скучно. Есть и более простое решение. Функциональные библиотеки позволяют создать новые функциональные объекты, связывая (bind) другие функциональные объекты с данными, которые при вызове будут им переданы. В примере 3 показано, как при использовании библиотеки Boost.Bind можно упростить код примера 2, отказавшись от ручного кодирования функционального объекта.

 

// Пример 3

// Эта программа идентична программе

// из примера 2, кроме того, что

// использует Boost.Bind

// при создании потока,

// принимающего параметры.

#include <boost/thread/thread.hpp>

#include <boost/thread/mutex.hpp>

#include <boost/bind.hpp>

#include <iostream>

boost::mutex io_mutex;

void count(int id)

{

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

{

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

std::cout << id << ": " <<i << std::endl;

}

}

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

{

boost::thread thrd1(boost::bind(&count, 1));

boost::thread thrd2(boost::bind(&count, 2));

thrd1.join();

thrd2.join();

return 0;

}

Условные переменные

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

Условная переменная всегда используется в связке с мьютексом и разделяемым ресурсом (или ресурсами). Поток первым делом блокирует мьютекс, а затем проверяет, находится ли ресурс в состоянии, допускающем его безопасное использование требуемым образом. Если он не в нужном состоянии, поток вызывает для условной переменной операцию ожидания. Эта операция приводит к разблокированию мьютекса во время ожидания, так что другой поток получает возможность изменить состояние разделяемого ресурса. Она также гарантирует, что при возвращении потока после ожидания мьютекс окажется заблокированным. Когда другой поток изменяет состояние разделяемого ресурса, он должен уведомить потоки, которые могут ждать условную переменную, позволяя им завершить ожидание.

Пример 4 иллюстрирует применение класса boost::condition. Определен класс, реализующий ограниченный буфер — контейнер фиксированного размера с поддержкой ввода/вывода в порядке очереди (FIFO). Этот буфер сделан потокобезопасным благодаря использованию boost::mutex. Операции put и get используют условную переменную, чтобы удостовериться в том, что поток будет ждать, пока буфер не окажется в состоянии, необходимом для завершения операции. Создаются два потока, один помещает в этот буфер сто целых, а другой их же извлекает. Ограниченный буфер в каждый момент времени может содержать только 10 целых, поэтому каждому из потоков приходится периодически ждать другой поток. Для проверки того, что это действительно происходит, операции put и get выводят в std::cout диагностические сообщения. Наконец, основной поток ожидает завершения обоих потоков.

// Пример 4

#include <boost/thread/thread.hpp>

#include <boost/thread/mutex.hpp>

#include <boost/thread/condition.hpp>

#include <iostream>

const int BUF_SIZE = 10;

const int ITERS = 100;

boost::mutex io_mutex;

class buffer

{

public:

typedef boost::mutex::scoped_lock scoped_lock;

buffer(): p(0), c(0), full(0) { }

void put(int m)

{

scoped_lock lock(mutex);

if (full == BUF_SIZE)

{

{

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

std::cout <<"Buffer is full. Waiting..."<< std::endl;

}

while (full == BUF_SIZE)

cond.wait(lock);

}

buf[p] = m;

p = (p+1) % BUF_SIZE;

++full;

cond.notify_one();

}

int get()

{

scoped_lock lk(mutex);

if (full == 0)

{

{

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

std::cout << "Buffer is empty. Waiting..."

<< std::endl;

}

while (full == 0)

cond.wait(lk);

}

int i = buf[c];

c = (c+1) % BUF_SIZE;

--full;

cond.notify_one();

return i;

}

private:

boost::mutex mutex;

boost::condition cond;

unsigned int p, c, full;

int buf[BUF_SIZE];

};

buffer buf;

void writer()

{

for (int n = 0; n < ITERS; ++n)

{

{

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

std::cout << "sending: " << n << std::endl;

}

buf.put(n);

}

}

void reader()

{

for (int x = 0; x < ITERS; ++x)

{

int n = buf.get();

{

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

std::cout << "received: " << n << std::endl;

}

}

}

 

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

{

boost::thread thrd1(&reader);

boost::thread thrd2(&writer);

thrd1.join();

thrd2.join();

return 0;

}

 

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

Пример 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;

}

 



Поделиться:


Последнее изменение этой страницы: 2016-12-11; просмотров: 815; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.118.126.241 (0.024 с.)