Обеспечение работы только одного экземпляра функции?

Я только вхожу в параллельное программирование. Скорее всего, моя проблема очень распространена, но, поскольку я не могу найти для нее подходящего названия, я не могу ее погуглить.

У меня есть приложение C ++ UWP, в котором я пытаюсь применить шаблон MVVM, но я предполагаю, что шаблон или даже UWP не имеет значения.

Во-первых, у меня есть сервисный интерфейс, который представляет операцию:

struct IService
{
virtual task<int> Operation() = 0;
};

Конечно, я предоставляю конкретную реализацию, но она не актуальна для этого обсуждения. Операция потенциально длительная: она отправляет HTTP-запрос.

Затем у меня есть класс, который использует службу (опять же, нерелевантные детали опущены):

class ViewModel
{
unique_ptr<IService> service;
public:
task<void> Refresh();
};

Я использую сопрограммы:

task<void> ViewModel::Refresh()
{
auto result = co_await service->Operation();
// use result to update UI
}

Функция Refresh вызывается по таймеру каждую минуту или в ответ на запрос пользователя. То, что я хочу: если операция обновления уже выполняется, когда запускается или запрашивается новая, то отмените вторую и просто дождитесь завершения первой (или тайм-аут). Другими словами, я не хочу ставить все вызовы в очередь на обновление — если вызов уже выполняется, я предпочитаю пропустить вызов до следующего таймера.

Моя попытка (вероятно, очень наивная) была:

mutex refresh;
task<void> ViewModel::Refresh()
{
unique_lock<mutex> lock(refresh, try_to_lock);
if (!lock)
{
// lock.release(); commented out as harmless but useless => irrelevant
co_return;
}
auto result = co_await service->Operation();
// use result to update UI
}

Редактировать после исходного поста: я закомментировал строку во фрагменте кода выше, так как это не имеет значения. Вопрос все тот же.

Но, конечно, утверждение неверно: unlock of unowned mutex, Я думаю, что проблема заключается в unlock из mutex от unique_lock деструктор, который происходит при продолжении сопрограммы и в другом потоке (кроме того, на котором он был изначально заблокирован).

Использование Visual C ++ 2017.

5

Решение

использование std::atomic_bool:

std::atomic_bool isRunning = false;
if (isRunning.exchange(true, std::memory_order_acq_rel) == false){
try{
auto result = co_await Refresh();
isRunning.store(false, std::memory_order_release);
//use result
}
catch(...){
isRunning.store(false, std::memory_order_release);
throw;
}
}

Два возможных улучшения: упаковка isRunning.store в классе RAII и использовать std::shared_ptr<std::atomic_bool> если время жизни, если atomic_bool находится в области видимости

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]