开发者

Multiple threads queuing for global lock should all return true once first lock acquired

开发者 https://www.devze.com 2023-02-20 18:08 出处:网络
A similar problem is this one: Are threads waiting on a lock FIFO? However, in this problem, once the lock is acquired only one thread executes the protected code, and in the end all threads will have

A similar problem is this one: Are threads waiting on a lock FIFO? However, in this problem, once the lock is acquired only one thread executes the protected code, and in the end all threads will have executed the开发者_运维知识库 code.

What I would like to do is to execute the protected code once, but for all threads queuing for the method call at that moment, return true.

Basically, the protected code is a global checkpoint, which is relevant for all threads waiting at that moment. I.e., doing N consecutive checkpoints would not achieve more than only 1.

Note that while the checkpointing is done, there will be other calls to the method, which themselves need a new checkpoint call.

I believe what I want to do is "batch-wise" synchronized calls to the global function.

How can I achieve this in C++, perhaps with Boost?


You seem to be looking for try_lock().

Given some Boost.Thread Lockable, a call to Lockable::try_lock() will return true if it can acquire the lock at that moment, otherwise false if it cannot acquire the lock.

When your thread reaches a checkpoint, have it try to acquire this lock. If it fails, another thread is already in the function. If it succeeds, check some bool to see if the checkpoint has already been run. If it has been run, release the lock and continue. If it hasn't been run, keep the lock and run the checkpoint function and set the checkpoint bool to true.


What you seem to want looks like a barrier which is provided by boost. However, if that doesn't help you, you can make something with condition variables, also in boost


Here is pseudo-code for how I would do it. I am assuming the existing of a mutex class with lock() and unlock() operations.

// This forward declaration helps with declaration
// of the "friend" status for the nested class.
class DoItOnce;

class DoItOnce
{
private:
    bool    m_amFirst;
    mutex   m_mutex;
    friend class ::DoItOnce::Op;    
public:
    DoItOnce()
    {
        m_amFirst = true;
        init(m_mutex);
    }
    ~DoItOnce() { destroy(m_mutex); }
    void reset()
    {
        m_mutex.lock();
        m_amFirst = true;
        m_mutex.lock();
    }

    //--------
    // Nested class
    //--------
    class Op {
    public:
        Op(DoItOnce & sync)
            : m_sync(sync)
        {
            m_sync.m_mutex.lock();
            m_amFirst = m_sync.m_amFirst;
            m_sync.m_amFirst = false;
        }
        ~Op() { m_sync.m_mutex.unlock(); }
        bool amFirst() { return m_amFirst; }
    private:
        DoItOnce &  m_sync;
        bool        m_amFirst;
    }; // end of nested class
}; // end of outer class

Here is an example to illustrate its intended use. You will implement the doWork() operation and have all your threads invoke it.

class WorkToBeDoneOnce
{
private:
    DoItOnce    m_sync;    
public:
    bool doWork()
    {
        DoItOnce::Op    scopedLock(m_sync);

        if (!scopedLock.amFirst()) {
            // The work has already been done.
            return true;
        }
        ... // Do the work
        return true;
    }
    void resetAmFirstFlag()
    {
        m_sync.reset();
    }
}

If you are confused by my use of the DoItOnce::Op nested class, then you can find an explanation of this coding idiom in my Generic Synchronisation Policies paper, which is available here in various formats (HTML, PDF and slides).

0

精彩评论

暂无评论...
验证码 换一张
取 消