代码之家  ›  专栏  ›  技术社区  ›  Rudransh Srivastava

为什么推送线程一直运行到队列达到最大大小?为什么它不像-推,砰,推,砰?

  •  1
  • Rudransh Srivastava  · 技术社区  · 1 年前

    我试图学习C++中的线程,却无法理解为什么两个线程没有一个接一个地运行?推送后,互斥体被解锁,并通过条件变量通知。 为什么此时pop线程没有执行?当互斥体解锁时,它可以访问队列并弹出插入的值。但相反,我看到的是,推送线程一直运行到队列满,之后,弹出线程运行到队列空。

     #include <iostream>
        #include <thread>
        #include <future>
        #include <queue>
     
    using namespace std;
     
    template<typename E>
    class BlockingQueue
    {
     
    private:
        size_t mMaxSize;
        queue<E> mQueue;
        mutex mtx;
        condition_variable mConditionVar;
     
    public:
        BlockingQueue(size_t size): mMaxSize{size}
        {
                    
        }
     
        void push(E element)
        {
            unique_lock<mutex> pushLock(mtx);
     
            mConditionVar.wait(pushLock, [this](){ return mQueue.size() < mMaxSize; });
     
            cout << "Pushing " << element << endl;
            mQueue.push(element);
     
            pushLock.unlock();
            mConditionVar.notify_one();
            
        }
     
        void pop()
        {
            unique_lock<mutex> popLock(mtx);
     
            mConditionVar.wait(popLock, [this](){ return mQueue.size() > 0; });
     
            cout << "Popping " << mQueue.front() << endl;
            mQueue.pop();
     
            popLock.unlock();
            mConditionVar.notify_one();
        }
     
        E front()
        {
            return mQueue.front();
        }
     
        size_t size()
        {
            return mQueue.size();
     
        }
     
    };
     
    int main()
    {
        BlockingQueue<int> bq(5);
     
        thread t1([&](){
            for (int i = 1; i <= 10; i++)
                bq.push(i);
        });
     
        thread t2([&](){
            for (int i = 1; i<=10; i++)
                bq.pop();
        });
     
        t1.join();
        t2.join();
     
        return 0;
    }
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Ted Lyngmo    1 年前

    什么时候? mConditionVar.notify_one(); 被调用时,它会唤醒另一个线程。两个线程现在都在运行,并将竞争获取锁。有时被唤醒的线程会得到它,有时上次拥有它的线程会再次得到它。

    如果你想让它们各运行一次,你需要改变条件。你可以添加一个成员变量, int m_turn = 0; ,并按如下方式使用:

    void push(E element) {
        unique_lock<mutex> pushLock(mtx);
    
        // check if it's my turn:
        mConditionVar.wait(pushLock, [this]() { return m_turn == 0; });
    
        cout << "Pushing " << element << endl;
        mQueue.push(element);
        m_turn = 1;            // it's the other thread's turn
    
        mConditionVar.notify_one();
    }
    
    void pop() {
        unique_lock<mutex> popLock(mtx);
    
        // check if it's my turn:
        mConditionVar.wait(popLock, [this]() { return m_turn == 1; });
    
        cout << "Popping " << mQueue.front() << endl;
        mQueue.pop();
        m_turn = 0;            // it's the other thread's turn
    
        mConditionVar.notify_one();
    }
    

    demo

        2
  •  0
  •   Tharuka Dilshan    1 年前

    由于线程的行为以及它们如何与条件变量和mutex交互。因此,为了确保更多的交替行为,您需要在解锁互斥体之前将notify_one()调用移动到锁定部分内。