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

Я использую 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.