Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ

Тут можно читать онлайн Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - бесплатно полную версию книги (целиком) без сокращений. Жанр: comp-programming, издательство ДМК Пресс, год 2012. Здесь Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
  • Автор:
  • Жанр:
  • Издательство:
    ДМК Пресс
  • Год:
    2012
  • Город:
    Москва
  • ISBN:
    978-5-94074-448-1
  • Рейтинг:
    5/5. Голосов: 11
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 100
    • 1
    • 2
    • 3
    • 4
    • 5

Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ краткое содержание

Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - описание и краткое содержание, автор Энтони Уильямс, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru
В наши дни компьютеры с несколькими многоядерными процессорами стали нормой. Стандарт С++11 языка С++ предоставляет развитую поддержку многопоточности в приложениях. Поэтому, чтобы сохранять конкурентоспособность, вы должны овладеть принципами и приемами их разработки, а также новыми средствами языка, относящимися к параллелизму.
Книга «Параллельное программирование на С++ в действии» не предполагает предварительных знаний в этой области. Вдумчиво читая ее, вы научитесь писать надежные и элегантные многопоточные программы на С++11. Вы узнаете о том, что такое потоковая модель памяти, и о том, какие средства поддержки многопоточности, в том числе запуска и синхронизации потоков, имеются в стандартной библиотеке. Попутно вы познакомитесь с различными нетривиальными проблемами программирования в условиях параллелизма.

Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - читать онлайн бесплатно полную версию (весь текст целиком)

Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - читать книгу онлайн бесплатно, автор Энтони Уильямс
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

void push(T const& data) {

counted_node_ptr new_node;

new_node.ptr = new node(data);

new_node.external_count = 1;

new_node.ptr->next = head.load(std::memory_order_relaxed);

while (!head.compare_exchange_weak(

new_node.ptr->next, new_node,

std::memory_order_release, std::memory_order_relaxed));

}

А что можно сказать о коде pop()? Чтобы получить желаемое отношение происходит-раньше, перед доступом к nextнеобходима операция с семантикой std::memory_order_acquireили более сильной. Указатель, который разыменовывается для доступа к полю next, — это прежнее значение, прочитанное операцией compare_exchange_strong()в increase_head_count(), поэтому указанная семантика нужна в случае успеха. Как и в push(), если обмен закончился неудачно, мы просто повторяем цикл, поэтому для отказа можно задать ослабленное упорядочение:

void increase_head_count(counted_node_ptr& old_counter) {

counted_node_ptr new_counter;

do {

new_counter = old_counter;

++new_counter.external_count;

}

while (!head.compare_exchange_strong(

old_counter, new_counter,

std::memory_order_acquire, std::memory_order_relaxed));

old_counter.external_count = new_counter.external_count;

}

Если вызов compare_exchange_strong()завершается успешно, то мы знаем, что раньше в поле ptrпрочитанного значения находилось то, что теперь хранится в переменной old_counter. Поскольку сохранение в push()было операцией освобождения, а данный вызов compare_exchange_strong()— операция захвата, то сохранение синхронизируется-с загрузкой, и мы имеем отношение происходит-раньше. Следовательно, сохранение в поле ptrв push()происходит-раньше доступа к ptr->nextв pop(), и мы в безопасности.

Отметим, что для этого анализа порядок доступа к памяти в начальном вызове head.load()не имел значения, поэтому в нем безопасно задать семантику std::memory_order_relaxed.

Далее на очереди операция compare_exchange_strong(), которая записывает в headзначение old_head.ptr->next. Нужно ли наложить на нее какие-нибудь ограничения, чтобы гарантировать целостность данных в этом потоке? Если обмен завершается успешно, то мы обращаемся к ptr->data, поэтому должны быть уверены, что сохранение ptr->dataв потоке, выполняющем push(), происходит-раньше загрузки. Но такая уверенность уже есть: операция захвата в increase_head_count()гарантирует, что существует отношение синхронизируется-с между сохранением в потоке, выполняющем push(), и операцией сравнения с обменом. Поскольку сохранение данных в потоке, выполняющем push(), расположено перед сохранением head, а вызов increase_head_count()расположен перед загрузкой ptr->data, то отношение происходит-раньше имеет место, и всё будет хорошо, даже если для операции сравнения с обменом в pop()задана семантика std::memory_order_relaxed. Есть еще всего одно место, где изменяется ptr->data— тот самый вызов swap(), на который вы сейчас смотрите, и ни один другой поток не может оперировать тем же узлом — в этом и заключается смысл сравнения с обменом.

Если compare_exchange_strong()завершается неудачно, то к новому значению old_headне будет обращений до следующей итерации цикла, и, поскольку мы уже решили, что семантики std::memory_order_acquireхватало в increase_head_count(), то здесь будет достаточно std::memory_order_relaxed.

Что можно сказать насчёт других потоков? Нужны ли более сильные ограничения, чтобы и другие потоки работали безопасно? Нет, не нужны, потому что headмодифицируется только операциями сравнения с обменом. Будучи операциями чтения-модификации-записи, они составляют часть последовательности освобождений, начатой операцией сравнения с обменом в push(). Поэтому compare_exchange_weak()в push()синхронизируется-с операцией compare_exchange_strong()в increase_head_count(), которая прочитает сохраненное значение, даже если в промежутке другие потоки изменят head.

Мы почти закончили, осталось только рассмотреть функции, в которых используются операции fetch_add(), изменяющие счетчик ссылок. Поток, который добрался до возврата данных из узла, может продолжать в твердой уверенности, что никакой другой поток не сможет модифицировать хранящиеся в узле данные. Однако любой поток, который потерпел неудачу при извлечении данных, знает, что какой-то другой поток данные в узле модифицировал ; он использовал функцию swap()для извлечения данных. Следовательно, чтобы предотвратить гонку за данными мы должны гарантировать, что swap()происходит-раньше delete. Чтобы добиться этого, проще всего задать семантику std::memory_order_releaseпри вызове fetch_add()в ветви, где мы возвращаем данные, и семантику std::memory_order_acquire— в ветви, где мы возвращаемся в начало цикла. Однако даже это перебор — лишь один поток выполняет delete(тот, что сбросил счетчик в нуль), поэтому только этому потоку нужно выполнить операцию захвата. К счастью, поскольку fetch_add()— операция чтения-модификации-записи, то она составляет часть последовательности освобождений, поэтому для достижения цели нам достаточно дополнительной операции load(). Если в ветви, где происходит возврат в начало цикла, счетчик ссылок уменьшается до нуля, то здесь же можно перезагрузить счетчик ссылок с семантикой std::memory_order_acquire, чтобы обеспечить требуемое отношение синхронизируется-с, а в самой операции fetch_add()достаточно задать std::memory_order_relaxed. Окончательная реализация стека с новой версией pop()приведена ниже.

Листинг 7.12.Свободный от блокировок стек с подсчётом ссылок и ослабленными атомарными операциями

template

class lock_free_stack {

private:

struct node;

struct counted_node_ptr {

int external_count;

node* ptr;

};

struct node {

std::shared_ptr data;

std::atomic internal_count;

counted_node_ptr next;

node(T const& data_):

data(std::make_shared(data_)), internal_count(0) {}

};

std::atomic head;

void increase_head_count(counted_node_ptr& old_counter) {

counted_node_ptr new_counter;

do {

new_counter = old_counter;

++new_counter.external_count;

}

while (!head.compare_exchange_strong(old_counter, new_counter,

std::memory_order_acquire,

std::memory_order_relaxed));

old_counter.external_count = new_counter.external_count;

}

public:

~lock_free_stack() {

while(pop());

}

void push(T const& data) {

counted_node_ptr new_node;

new_node.ptr = new node(data);

new_node.external_count = 1;

new_node.ptr->next = head.load(std::memory_order_relaxed);

while (!head.compare_exchange_weak(

new_node.ptr->next, new_node,

std::memory_order_release,

std::memory_order_relaxed));

}

std::shared_ptr pop() {

counted_node_ptr old_head =

head.load(std::memory_order_relaxed);

for (;;) {

increase_head_count(old_head);

node* const ptr = old_head.ptr;

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Энтони Уильямс читать все книги автора по порядку

Энтони Уильямс - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




Параллельное программирование на С++ в действии. Практика разработки многопоточных программ отзывы


Отзывы читателей о книге Параллельное программирование на С++ в действии. Практика разработки многопоточных программ, автор: Энтони Уильямс. Читайте комментарии и мнения людей о произведении.


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x