std::mutex在std::cout上无法正确锁定

4

我正在学习C++11中的多线程技术,我尝试了这个简单的测试,但输出结果并不是我预期的。

#include <thread>
#include <iostream>
#include <mutex>

int main() {
    auto function = [](int x) {
        std::mutex m;
        m.try_lock();
        std::cout << "Hello" << x << std::endl;
        m.unlock();
        return;
    };
    std::thread t1(function , 1);
    std::thread t2(function, 2);
    std::thread t3(function, 3);
    std::thread t4(function, 4);    

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    std::cin.get();
    return 0;
}

我预期的输出结果是:
Hello1
Hello2
Hello3
Hello4

但是我得到了类似于这样的结果:

(也许不是按照这个顺序,但每个 "hello" 和数字都在单独的一行)

HelloHello21
Hello3
Hello4

或者

HelloHello2
1
Hello3
Hello4

除了互斥锁明显没有正确锁定之外,还有什么谜团呢?而且总是Hello1被切成两半。

编辑:使用VS2015完成,如果有什么区别(应该没有),请注意它全部都是标准的。


1
除了Sam已经说的,你知道每次调用函数都会得到它自己独有的本地变量副本,对吧?所以在这种情况下,每个线程都有自己独立的互斥锁。毫不奇怪,锁定一个互斥锁不会锁定其他任何互斥锁。 - user253751
建议将互斥锁放在与要锁定的对象相同的作用域中,以避免此类错误。 - M.M
std::mutex m; 的定义移到函数外部。 - Nicholas Hamilton
2个回答

3
您需要使用static,因为所有线程必须访问同一互斥对象。此外,如果在到达unlock()之前抛出异常,则try_lock()(或lock())会导致问题。推荐使用std::lock_guard<>(),这是一种更安全的方法,当超出其范围时,无论是通过函数返回还是通过异常,都将被释放。请尝试以下函数的修订版:
auto function = [](int x) {
    static std::mutex m;
    std::lock_guard<std::mutex> mylock(m);
    std::cout << "Hello" << x << std::endl;
    return;
};

请参见http://en.cppreference.com/w/cpp/thread/lock_guard获取更多信息。
此外,如果您认为未来可能会更改互斥类型(或仅想尝试一下花哨的东西),则可以使用推断模板类型设置互斥量:
std::lock_guard<decltype(m)> mylock(m);

工作得很好,谢谢!不过我还是想知道为什么只使用互斥量就行不通 :) - Stephanos Philippides
1
@StephanosPhilippides 你创建了4个独立的互斥锁,应该只使用1个互斥锁。 - M.M
1
小心使用静态mutex,因为在某些情况下会导致“互斥锁在忙碌时被销毁”(https://stackoverflow.com/a/22862067/1100913)。更好的方法是在主函数中创建它并将其作为参数传递给线程。 - Andrey

2

try_lock()并不一定会锁定互斥量。它可能会锁定互斥量,也可能不会。它只会在能够锁定互斥量时尝试锁定互斥量。如果已经被锁定,它不会等待互斥量解锁,而是返回一个指示已发生这种情况的标志。

因此,实际上,您的代码并没有以任何方式、任何形式有效地同步其输出。

如果你真的想锁定互斥量,请使用lock()而不是try_lock()

编辑:正如其他人指出的那样,您的代码实际上为每个线程创建了一个单独的互斥量,因此需要将该互斥量设为static


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接