在释放互斥锁上之前或之后通知消费者线程?

时间:2016-01-05 20:16:59

标签: c++ multithreading c++11 mutex

下面是使用条件变量定义线程安全队列的 C ++并发操作一书中的清单4.5。

#include <mutex>
#include <condition_variable>
#include <queue>
#include <memory>

template<typename T>
class threadsafe_queue
{
private:
    mutable std::mutex mut;
    std::queue<T> data_queue;
    std::condition_variable data_cond;
public:
    threadsafe_queue()
    {}
    threadsafe_queue(threadsafe_queue const& other)
    {
        std::lock_guard<std::mutex> lk(other.mut);
        data_queue=other.data_queue;
    }

    void push(T new_value)
    {
        std::lock_guard<std::mutex> lk(mut);
        data_queue.push(new_value);
        data_cond.notify_one();
    }

    void wait_and_pop(T& value)
    {
        std::unique_lock<std::mutex> lk(mut);
        data_cond.wait(lk,[this]{return !data_queue.empty();});
        value=data_queue.front();
        data_queue.pop();
    }

std::shared_ptr<T> wait_and_pop()
{
    std::unique_lock<std::mutex> lk(mut);
    data_cond.wait(lk,[this]{return !data_queue.empty();});
    std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
    data_queue.pop();
    return res;
}

bool try_pop(T& value)
{
    std::lock_guard<std::mutex> lk(mut);
    if(data_queue.empty)
        return false;
    value=data_queue.front();
    data_queue.pop();
}

std::shared_ptr<T> try_pop()
{
    std::lock_guard<std::mutex> lk(mut);
    if(data_queue.empty())
        return std::shared_ptr<T>();
    std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
    data_queue.pop();
    return res;
}

bool empty() const
{
    std::lock_guard<std::mutex> lk(mut);
    return data_queue.empty();
}
};

int main()
{}

在推送功能中,当互斥锁仍处于锁定状态时,正在通知另一个线程。在互斥体发布后立即通知popper线程不是更好。例如,像这样:

void push(T new_value)
{
    {
        std::lock_guard<std::mutex> lk(mut)
        data_queue.push(new_value);
    }
    data_cond.notify_one();
}

我知道他们在功能上做同样的事情。我正在考虑原始情况,消费者线程将收到错误通知,最终使其尝试多次尝试锁定互斥锁。但是,在第二种情况下,我们避免尽早唤醒消费者线程,因此,尝试锁定互斥锁可以在第一次成功。

2 个答案:

答案 0 :(得分:1)

无关紧要,最终结果是一样的。如果您首先通知并解锁互斥锁,则会通知侦听线程,但仍会锁定在互斥锁上,一旦互斥锁解锁,线程就会继续。

如果您首先解锁互斥锁,线程将不会知道该条件已准备就绪,直到通知它并等待条件变量(禁止虚假唤醒)。

最后,只有在满足两个条件时才会继续监听线程 - 互斥锁被解锁,变量被通知。

答案 1 :(得分:0)

前面的答案是错误的,您最初的断言是正确的。最好先解锁,再通知。 虽然一些实现可以避免匆忙等待问题,特别是一些 pthreads 实现,但大多数不能。

如果您关注延迟而不是吞吐量,这会对实际性能产生重大影响。