Я прочитал в листинге 7.15 «Параллелизм в C++»:
Использование схемы подсчета ссылок позволяет избежать этой конкретной гонки, но это не единственная гонка. в толчке(). Если вы посмотрите на обновленную версию push() в листинге 7.15, вы увидите шаблон, который вы видели в стеке: загрузите атомарный указатель (1) и разыменуйте (2) этот указатель. Тем временем другой поток может обновить указатель (3), что в конечном итоге приведет к освобождению узла (в pop()). Если узел освобождается до того, как вы разыменовываете указатель, у вас неопределенное поведение. Ой! Соблазнительно добавить внешний счетчик в хвост так же, как вы это сделали для головы, но каждый узел уже имеет внешний счетчик в следующем указателе предыдущего узла в очереди. Наличие двух внешних счетчиков для одного и того же узла требует изменения схемы подсчета ссылок, чтобы избежать слишком раннего удаления узла. Вы можете решить эту проблему, также подсчитав количество внешних счетчиков внутри структуры узла и уменьшив это число при уничтожении каждого внешнего счетчика (а также добавив соответствующий внешний счетчик к внутреннему счетчику). Если внутренний счетчик равен нулю и внешние счетчики отсутствуют, вы знаете, что узел можно безопасно удалить. С этой техникой я впервые столкнулся в проекте Джо Сейя Atomic Ptr Plus (http://atomic-ptr-plus.sourceforge.net/). Следующий листинг показывает, как push() выглядит в соответствии с этой схемой.
Листинг 7.15. (Неработающая) первая попытка пересмотра push():
void push(T new_value)
{
std::unique_ptr new_data(new T(new_value));
counted_node_ptr new_next;
new_next.ptr = new node;
new_next.external_count = 1;
for (;;)
{
node* const old_tail = tail.load(); // 1
T* old_data = nullptr;
if (old_tail->data.compare_exchange_strong(
old_data, new_data.get())) // 2
{
old_tail->next = new_next;
tail.store(new_next.ptr); // 3
new_data.release();
break;
}
}
}
Однако я не совсем понимаю эту часть:
Если узел освобождается до того, как вы разыменовывает указатель, у вас неопределенное поведение.
В pop() функция сначала проверяет, указывают ли заголовок и хвост на одно и то же узел-заполнитель. Если да, то он определяет, что очередь пуста, и ничего не делает. В листинге 7.15 функция push() обновляет хвост только на последнем шаге, а это означает, что pop() не будет работать с узлом, поставленным в очередь, пока push() не завершит операцию постановки в очередь. Итак, откуда же берется это состояние гонки?
Поскольку автор не привел в книге пример предположения для этой ситуации (пример основан на предыдущем блокировочном стеке ), я не могу предоставить полный контекст. Ниже приведен код pop() до изменения.
template
class lock_free_queue {
private:
struct node {
std::shared_ptr data;
node *next;
node() : next(nullptr) {}
};
std::atomic head;
std::atomic tail;
node *pop_head() {
node *const old_head = head.load();
if (old_head == tail.load()) {
return nullptr;
}
head.store(old_head->next);
return old_head;
}
public:
lock_free_queue() : head(new node), tail(head.load()) {}
// ...
std::shared_ptr pop() {
node *old_head = pop_head();
if (!old_head) {
return std::shared_ptr();
}
std::shared_ptr const res(old_head->data);
delete old_head;
return res;
}
void push(T new_value) {
std::shared_ptr new_data(std::make_shared(new_value));
node *p = new node;
node *const old_tail = tail.load();
old_tail->data.swap(new_data);
old_tail->next = p;
tail.store(p);
}
};
Подробнее здесь: https://stackoverflow.com/questions/793 ... concurrenc
Проблема, упомянутая в функции push очереди без блокировок в разделе 7.15 «Параллелизм C++ в действии». ⇐ C++
Программы на C++. Форум разработчиков
1736249524
Anonymous
Я прочитал в листинге 7.15 «Параллелизм в C++»:
Использование схемы подсчета ссылок позволяет избежать этой конкретной гонки, но это не единственная гонка. в толчке(). Если вы посмотрите на обновленную версию push() в листинге 7.15, вы увидите шаблон, который вы видели в стеке: загрузите атомарный указатель (1) и разыменуйте (2) этот указатель. Тем временем другой поток может обновить указатель (3), что в конечном итоге приведет к освобождению узла (в pop()). Если узел освобождается до того, как вы разыменовываете указатель, у вас неопределенное поведение. Ой! Соблазнительно добавить внешний счетчик в хвост так же, как вы это сделали для головы, но каждый узел уже имеет внешний счетчик в следующем указателе предыдущего узла в очереди. Наличие двух внешних счетчиков для одного и того же узла требует изменения схемы подсчета ссылок, чтобы избежать слишком раннего удаления узла. Вы можете решить эту проблему, также подсчитав количество внешних счетчиков внутри структуры узла и уменьшив это число при уничтожении каждого внешнего счетчика (а также добавив соответствующий внешний счетчик к внутреннему счетчику). Если внутренний счетчик равен нулю и внешние счетчики отсутствуют, вы знаете, что узел можно безопасно удалить. С этой техникой я впервые столкнулся в проекте Джо Сейя Atomic Ptr Plus (http://atomic-ptr-plus.sourceforge.net/). Следующий листинг показывает, как push() выглядит в соответствии с этой схемой.
Листинг 7.15. (Неработающая) первая попытка пересмотра push():
void push(T new_value)
{
std::unique_ptr new_data(new T(new_value));
counted_node_ptr new_next;
new_next.ptr = new node;
new_next.external_count = 1;
for (;;)
{
node* const old_tail = tail.load(); // 1
T* old_data = nullptr;
if (old_tail->data.compare_exchange_strong(
old_data, new_data.get())) // 2
{
old_tail->next = new_next;
tail.store(new_next.ptr); // 3
new_data.release();
break;
}
}
}
Однако я не совсем понимаю эту часть:
Если узел освобождается до того, как вы разыменовывает указатель, у вас неопределенное поведение.
В pop() функция сначала проверяет, указывают ли заголовок и хвост на одно и то же узел-заполнитель. Если да, то он определяет, что очередь пуста, и ничего не делает. В листинге 7.15 функция push() обновляет хвост только на последнем шаге, а это означает, что pop() не будет работать с узлом, поставленным в очередь, пока push() не завершит операцию постановки в очередь. Итак, откуда же берется это состояние гонки?
Поскольку автор не привел в книге пример предположения для этой ситуации (пример основан на предыдущем блокировочном стеке ), я не могу предоставить полный контекст. Ниже приведен код pop() до изменения.
template
class lock_free_queue {
private:
struct node {
std::shared_ptr data;
node *next;
node() : next(nullptr) {}
};
std::atomic head;
std::atomic tail;
node *pop_head() {
node *const old_head = head.load();
if (old_head == tail.load()) {
return nullptr;
}
head.store(old_head->next);
return old_head;
}
public:
lock_free_queue() : head(new node), tail(head.load()) {}
// ...
std::shared_ptr pop() {
node *old_head = pop_head();
if (!old_head) {
return std::shared_ptr();
}
std::shared_ptr const res(old_head->data);
delete old_head;
return res;
}
void push(T new_value) {
std::shared_ptr new_data(std::make_shared(new_value));
node *p = new node;
node *const old_tail = tail.load();
old_tail->data.swap(new_data);
old_tail->next = p;
tail.store(p);
}
};
Подробнее здесь: [url]https://stackoverflow.com/questions/79335474/the-issue-mentioned-in-the-push-function-of-a-lock-free-queue-in-c-concurrenc[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия