Многопоточность, стоящая в очереди для глобальной блокировки, должна все возвращать true после первой блокировки - PullRequest
1 голос
/ 29 марта 2011

Аналогичная проблема заключается в следующем: Ожидают ли потоки блокировки FIFO? Однако в этой проблеме после получения блокировки только один поток выполняет защищенный код, и в конце все потоки будутЯ выполнил код.

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

По сути, защищенный код является глобальной контрольной точкой, которая актуальна для всех потоков, ожидающих в данный момент.Т.е. выполнение N последовательных контрольных точек не приведет к достижению большего, чем только 1.

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

Я считаю, что я хочу сделать "синхронизированные по пакетам" вызовы глобальной функции.

Как я могу добиться этого в C ++, возможно, с помощью Boost?

Ответы [ 3 ]

1 голос
/ 29 марта 2011

То, что вам нужно, выглядит как барьер, который обеспечивается boost . Однако, если это вам не поможет, вы можете сделать что-то с условными переменными, также в boost

1 голос
/ 29 марта 2011

Вы, похоже, ищете try_lock().

Учитывая некоторое значение Boost.Thread Lockable, вызов Lockable::try_lock() вернет true, если он может получить блокировку в этот момент, в противном случае - false, если он не может получить блокировку.

Когда ваш поток достигает контрольной точки, попытайтесь получить эту блокировку. Если это не удается, другой поток уже находится в функции. Если это успешно, проверьте некоторые bool, чтобы увидеть, была ли контрольная точка уже запущена. Если он был запущен, снимите блокировку и продолжайте. Если он не был запущен, сохраните блокировку и запустите функцию контрольной точки и установите для контрольной точки bool значение true.

0 голосов
/ 30 марта 2011

Вот псевдокод того, как я это сделаю.Я предполагаю, что существует класс mutex с операциями lock() и unlock().

// 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

Вот пример, иллюстрирующий его предполагаемое использование.Вы реализуете операцию doWork(), и все ваши потоки будут вызывать ее.

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();
    }
}

Если вас смущает использование вложенного класса DoItOnce::Op, вы можете найти объяснение этой идиомы кодирования.в моем документе Generic Synchronization Policies , который доступен здесь в различных форматах (HTML, PDF и слайды).

...