How to implement a countdown latch in Qt?

2019-08-03 09:23发布

问题:

A countdown latch (a.k.a. CountDownLatch) is a synchronization primitive that ensures that all of the used resources are released before the primitive is destroyed. It is like a QSemaphore, but working in reverse: we want to block not to acquire a resource, but to ensure that all of them have been released.

What would be an easy way to implement it in Qt?

回答1:

Here's an implementation that leverages QSemaphore:

// https://github.com/KubaO/stackoverflown/tree/master/questions/countdown-latch-38362044
#include <climits>
#include <QSemaphore>

class CountDownLatch {
  Q_DISABLE_COPY(CountDownLatch)
  QSemaphore m_sem{INT_MAX};
public:
  CountDownLatch() {}
  ~CountDownLatch() {
     m_sem.acquire(INT_MAX);
     m_sem.release(INT_MAX);
  }
  class Locker {
    CountDownLatch * sem;
  public:
    Locker(const Locker & other) : sem{other.sem} { sem->m_sem.acquire(); }
    Locker(Locker && other) : sem{other.sem} { other.sem = nullptr; }
    Locker(CountDownLatch * sem) : sem{sem} { sem->m_sem.acquire(); }
    ~Locker() { if (sem) sem->m_sem.release(); }
  };
  Locker lock() { return Locker{this}; }
};

To use, keep an instance of CountdownLatch::Locker while you want the latch to stay blocked. The latch's destructor will block until all locker's are destroyed.

#include <QtConcurrent>

struct MyClass {
  CountDownLatch m_latch;
  MyClass() {
    auto lock = m_latch.lock(); // must be taken here
    QtConcurrent::run([this, lock]{
      // DON'T lock here, you'll have a race!
      QThread::sleep(10);
    });
  }
};

int main() {
  MyClass a;
}

The instance a will stay around until the concurrent worker is done, i.e. for 10 seconds.