Захват блокировки, действительный только один раз в С++

Я использую C++ 11, где у меня есть 4 потока, которые параллельно вставляются в параллельную очередь. Я знаю, когда обрабатываются потоки, т.е. Ожидаемый конечный размер очереди.

Теперь я хочу выполнить окончательную операцию агрегации содержимого содержимого очереди, которая должна выполняться строгоодин раз. Например, скажем, я хочу, чтобы агрегировать значения и POST его на внешнюю службу.

Как получить блокировку, которая действительна только один раз? Я не могу использовать простой мьютекс, потому что это не гарантирует мне единственное требование.

псевдокод:

// inside a thread
enqueue items to concurrent_queue
if(concurrent_queue.size() == EXPECTED_SIZE) { // do something ONLY once
}
3 ответа

Простое решение.

if(concurrent_queue.size() == EXPECTED_SIZE) { // do something ONLY once static bool doItOnce = DoItOnceOnly();
}


Проверьте подход от реализации Singleton. Самый простой:

bool executed = false;
void Execute()
{ Lock lock; // scope-based lock, released automatically when the function returns if (executed == false) { executed = true; //.. do computation }
}

Проверьте также решения с атомными переменными и двойной проверкой блокировки для повышения производительности: http://preshing.com/20130930/******-checked-locking-is-fixed-in-cpp11/


Если вас не беспокоит возможность сбоя операции, вы можете просто использовать atomic который связан с этой операцией. Все потоки попытаются изменить флаг с false на true с помощью compare_exchange_strong, но только один из них будет успешным:

// inside a thread
enqueue items to concurrent_queue
if(concurrent_queue.size() == EXPECTED_SIZE) { std::atomic<bool>& flag = retrieve_the_associated_bool(); bool expected = false; if (flag.compare_exchange_strong(expected, true)) { // do something }
}
</bool>

Если операция может завершиться неудачно, вы должны использовать std::call_once и std::once_flag связанные с операцией. Остальные потоки будут ждать, пока кто-то пытается "что-то сделать", и каждый пытается по очереди, пока вам не удастся:

// inside a thread
enqueue items to concurrent_queue
if(concurrent_queue.size() == EXPECTED_SIZE) { std::once_flag& flag = retrieve_the_associated_once_flag(); std::call_once(flag, []{ // do something }); // do something ONLY once
}

licensed under cc by-sa 3.0 with attribution.