【问题标题】:Ensuring that only one instance of a function is running?确保只有一个函数实例在运行?
【发布时间】:2017-10-01 02:39:57
【问题描述】:

我刚刚开始接触并发编程。很可能我的问题很常见,但由于我找不到一个好名字,我无法谷歌它。

我有一个 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
}

在原帖后编辑:我注释掉了上面代码 sn-p 中的行,因为它没有区别。问题还是一样。

当然,断言失败:unlock of unowned mutex。我猜问题出在mutex by unique_lock 析构函数的unlock 上,它发生在协程的延续和不同的线程上(除了它最初被锁定的线程)。

使用 Visual C++ 2017。

【问题讨论】:

  • @WhozCraig,我没试过,但我愿意接受任何建议——你能举个代码示例吗?
  • 真正幼稚的解决方案:std::atomic&lt;bool&gt; running.
  • 你不认为问题更多在于你试图释放一个你知道没有锁定的锁吗?
  • 制作一个静态实例计数器怎么样?
  • @UKMonkey,不,问题不在于锁的释放。原因很多,所以我们只能说即使删除了线路,问题仍然存在。我相应地编辑了问题。

标签: c++ multithreading c++11 visual-c++ visual-c++-2017


【解决方案1】:

使用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 类中,如果atomic_bool 是作用域的,则使用std::shared_ptr&lt;std::atomic_bool&gt;

【讨论】:

  • 解决方案有效,没有其他的(至少不是这里),所以我接受它。但是我有两个cmets。 1)我对标准库不提供标准解决方案这一事实感到失望。这并不是要降低答案的价值。 2)std::atomic_bool不能用std::atomic_flag代替吗? std:atomic_bool 提供更多功能(更多控制),但它提供了哪些附加价值?明白它是某种优化,但它有多重要?
  • 1) 这 一个标准解决方案 2) std::atomic_flag 可以在这里使用,虽然我个人希望 std::atomic_flag 只能与 std::call_once 一起使用。
猜你喜欢
  • 2010-09-27
  • 2011-09-05
  • 1970-01-01
  • 2020-09-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多