Страницы

Поиск по вопросам

среда, 25 декабря 2019 г.

C++ реализация call_once

#cpp #mutex #lock_free


Хочу разобраться в том, как работает std::call_once. И главное - lock-free ли он.
Здесь пытаются его реализовать с использованием мьютекса. Если call_once можно реализовать
только с использованием мьютекса, какие проблемы могут возникнуть с этим кодом?

#include 
#include 
#include 

using namespace std;
using my_once_flag = atomic;

void my_call_once(my_once_flag& flag, std::function foo) {
    bool expected = false;
    bool res = flag.compare_exchange_strong(expected, true,
                                            std::memory_order_release, std::memory_order_relaxed);
    if(res)
        foo();
}
my_once_flag flag;
void printOnce() {
    my_call_once(flag, [](){
       cout << "test" << endl;
    });
}
int main() {
    for(int i = 0; i< 50; ++i){
            thread([](){
                printOnce();
            }).detach();
    }
    return 0;
}

    


Ответы

Ответ 1



Стандарт не налагает ограничений на реализацию std::call_once поэтому, его реализация может быть как с блокировками, так и без(я не знаю, возможно ли такую реализацию придумать). Что касается Вашей реализации: она просто неверна. Пусть у нас будет 2 потока, которые одновременно заходят в функцию и попадают на строчку: flag.compare_exchange_strong один из них выставит флаг, а другой уйдёт с полной уверенностью, что функция уже была вызвана. Но до вызова функции дело ещё вообще не дошло! Поэтому, в правильной реализации, на входе в call_once все потоки должны выстроиться в очередь, если кто-то уже начал выполнение функции. Конечно, если бы функция была «чистой»(pure) можно было бы устроить спекулятивное выполнение, с принятием результата от того потока, что первым закончит её исполнение. Но стандарт не налагает никаких ограничений на функцию, которая может быть исполнена в call_once. Поэтому, в целом, я не вижу как можно её реализовать в неблокирующем виде.

Комментариев нет:

Отправить комментарий