LINUX.ORG.RU

Возврат значения из другого потока

 ,


0

6

Добрый день.

#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

void f(int &ret)
{
    ret = 43;
    atomic_thread_fence(memory_order_release);
}

int main()
{
    int ret;
    thread t(f, ref(ret));
    t.join();
    atomic_thread_fence(memory_order_acquire);
    cout << ret << endl;
}
Нужны ли здесь барьеры (atomic_thread_fence)?
P.S: код некорректен.

★★

Последнее исправление: pavlick (всего исправлений: 3)

нет

нет, не нужны, и да, Павлик, вы вообще в курсе что такое барьеры и когда их необходимо использовать? Если нет - то крайне рекомендую вам ознакомиться с трудами сэра Энтони Уильямса о многопоточности и модели памяти в C++

peacelove
()
Ответ на: нет от peacelove

Может представите больше аргументов? В чём я не прав? В чём ошибаюсь? Из справочника:

Thread fence Establishes a multi-thread fence: The point of call to this function becomes either an acquire or a release synchronization point (or both).

All visible side effects from the releasing thread that happen before the call to this function are synchronized to also happen before the call this function in the acquiring thread.

Calling this function has the same effects as a load or store atomic operation, but without involving an atomic value.

Считаю, что барьеры здесь нужну (если в thread не вшиты никакие синхронизации, подобно как у mutex)

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Павлик, прочти книгу. Она одна. Хватит её на десять лет вперёд.

Аргумент один - ты вообще не понимаешь о чём идёт речь. Это барьеры для atomicов. Когда найдёшь у себя в коде хоть один atomic отпишись сюда.

peacelove
()

По-моему это вы не понимаете о чём говорите. Синхронизация нужна не для атомиков, она на них лишь построена (но можно и без них используя барьеры). Из справочника http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence

Establishes memory synchronization ordering of non-atomic and relaxed atomic accesses ...

Со справочником тоже спорить будете?

pavlick ★★
() автор топика

В данном случае потоки синхронизированы join.

anonymous
()

Ребят, если не сложно, то приводите, пожалуйста, доказательства/ссылки.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Павлик хороший

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

peacelove
()

Sierra Tango Oscar Papa



Watch the clock (ticking slowly)
Beta block (fast beating heart)
Thoughts freeze (oh so many)
Rest in peace (falling apart)

No sleep

Electro shock (shock, shock)
Heart stopps (restless soul)
Lifesigns cease (in nomine patris)
Rest in peace (full control)

No sleep

Wrist slashed (pulsating blood)
Worlds collapse (deep cut)
Voices mute (pst, pst!)
Sleep for good (silent at last)

Insomniacs last act ...
what sort of dream will you attract?

Sleepless and obsessed with purple
(Why do I feel so)
anonymous
()

Из стандарта:

30.3.1.5 Synchronization: The completion of the thread represented by *this synchronizes with (1.10) the corresponding successful join() return. [Note: Operations on *this are not synchronized. —end note]

Понимаю так, что в данном случаи барьеры не нужны. join() производит синхронизацию.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Доказательства

У тебя два потока и одна общая переменная. Пока работает второй поток, первый ждёт (join), т.е. они не могут обратиться к этой переменной одновременно. Здесь вообще не нужна никакая синхронизация.

anonymous
()
Ответ на: Павлик хороший от peacelove

Ты как царь, только похуже. Будешь князем )))))))))))))))))))0

anonymous
()
Ответ на: Доказательства от anonymous

Доказательства

У тебя два потока и одна общая переменная. Пока работает второй поток, первый ждёт (join), т.е. они не могут обратиться к этой переменной одновременно. Здесь вообще не нужна никакая синхронизация.

Она не нужна только потому, что барьеры раставили разработчики std библиотеки. Сам факт ожидания ничего не гарантирует, кеши нужно синхронизировать.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Когда у тебя join() вернул, остался только 1 поток. Что ты там синхронизировать-то собрался? Это уже к вопросу о разработке этого вашего pthreads или что там используется. Не думай, что ты умнее разрабов этих библиотеких ваших много

anonymous
()

Возврат значения из другого потока

Почему-то сразу подумал про std::future, но так про него ничего и не увидел в этой теме.

asaw ★★★★★
()

нет бы использовать нормальные языки

package main

func main() {
	ch := make(chan int)
	go func() { ch <- 666 } ()
	print("Got ", <- ch)
}
mix_mix ★★★★★
()

Аргумент один - ты вообще не понимаешь о чём идёт речь. Это барьеры для atomicов. Когда найдёшь у себя в коде хоть один atomic отпишись сюда.

Вы были правы, atomic_thread_fence() действительно завязан на atomic, был не прав, признаю.

pavlick ★★
() автор топика
Ответ на: комментарий от m0rph

За ссылку отдельное спасибо. Впервые встречаю свежий материал на русском.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

atomic_thread_fence() не завязан ни на каком атомике - это вещи ортогональные. Хотя и для правильной работы как правило должны присутствовать оба.

placement_new ★★
()
volatile bool ready = false;
int data = 0;

int main()
{
    std::thread t1([&]() {
        data = 42;
        std::atomic_thread_fence(std::memory_order_release);
        ready = true;
    });
    std::thread t2([&]() {
        while (!ready) {
            std::this_thread::yield();
        }
        std::atomic_thread_fence(std::memory_order_acquire);
        assert(data == 42);
    });
    t1.join();
    t2.join();
}

У меня в коде нет ни одного атомика (явного:)), тем не менее код на x86 будет прекрасно работать.

placement_new ★★
()
Последнее исправление: placement_new (всего исправлений: 1)
Ответ на: комментарий от pavlick

Вы были правы

Нет, это Вы были правы. Барьер не нужен, потому что он и так уже неявно вставляется для того, чтобы следовать стандарту.

Sorcerer ★★★★★
()
Ответ на: комментарий от Sorcerer

Это да. Признал себя неправым в плане использования atomic_thread_fence. Если посмотреть в стандарт:

29.8 A release fence A synchronizes with an acquire fence B if there exist atomic operations X and Y, both operating on some atomic object M, such that A is sequenced before X, X modifies M, Y is sequenced before B, and Y reads the value written by X or a value written by any side effect in the hypothetical release sequence X would head if it were a release operation.

Т.е. для синхронизации с помощью atomic_thread_fence необходима конструкция:


std::atomic<bool> flag(false);

void thread_1()
{
    // что-то сохраняем в разделяемые области.

    atomic_thread_fence(std::memory_order_release);
    flag.store(true, std::memory_order_relaxed);
}
void thread_2()
{
    while(!flag.load(std::memory_order_relaxed));
    atomic_thread_fence(std::memory_order_acquire);

   // читаем из разделяемых областей.
}
Правда не понял - зачем нужны такие барьеры? Что изменится если задать order непосредственно в атомной операции (вместо relaxed)? В чём идея?

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Кстати, тут тоже написано
http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence

Notes atomic_thread_fence imposes different synchronization constraints than an atomic store operation with the same std::memory_order. While an atomic store-release operation prevents all preceding writes from moving past the store-release, an atomic_thread_fence with memory_order_release ordering prevents all preceding writes from moving past all subsequent stores.

placement_new ★★
()
Ответ на: комментарий от pavlick

Та же написано, что с их помощью ты можешь синхронизировать не atomic-переменные.

Establishes memory synchronization ordering of non-atomic and relaxed atomic accesses, as instructed by order, without an associated atomic operation.

То есть не оябзан ты иметь atomic-переменную (в терминах с++) как я тебе показал в примере выше, а просто гарантированную atomic-операцию на твоем cpu (как например, запись bool на x86).

Это просто store-store/load-load/store-load/load-store барьер или их комбинация (в зависимости от указанной тобою memory order).

placement_new ★★
()
Последнее исправление: placement_new (всего исправлений: 2)
Ответ на: комментарий от placement_new

Не согласен. Выше приводил цитату из стандарта, которая чётко говорит о необходимости наличия атомных операций, а atomic_thread_fence ставятся рядом с ними. Я там даже пример набросал. В стандарте не видел ни слова, что atomic_thread_fence можно использовать без atomic.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Там не говорится, что ты должен использовать std::atomic. Там написано "...exist atomic operations X and Y, both operating on some atomic object M..."

Полистай вот это

https://www.google.ru/url?sa=t&rct=j&q=&esrc=s&source=web&amp...

placement_new ★★
()
Последнее исправление: placement_new (всего исправлений: 1)
Ответ на: комментарий от placement_new

Итересная мысль, возможно так и есть. Прошёлся по стандарту в поисках определения atomic object, не нашёл. Вначале так и понимал барьеры, теперь не знаю что и думать.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Накопал статью, которая разъясняет отличия барьеров от атомной release/acquire http://preshing.com/20131125/acquire-and-release-fences-dont-work-the-way-youd-expect/ Коротко суть: имеем код

Singleton* tmp = new Singleton;
g_dummy.store(0, std::memory_order_release);
m_instance.store(tmp, std::memory_order_relaxed);
store(,memory_order_release) гарантирует, что предыдущие записи не случатся позже store-release. Но запись в m_instance может оказаться раньше других операций (даже раньше конструктора Singleton, с точки зрения других ядер).
Как может помочь барьер? Цитата из справочника:

Notes atomic_thread_fence imposes different synchronization constraints than an atomic store operation with the same std::memory_order. While an atomic store-release operation prevents all preceding writes from moving past the store-release, an atomic_thread_fence with memory_order_release ordering prevents all preceding writes from moving past all subsequent stores.

release барьер запрещает предыдущим записям случиться позже следующих. Т.е. с барьером запись m_instance не случится раньше предыдущих записей.

pavlick ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.