2017-10-13 52 views
2

我认为这个代码(我使用互斥体在线程之间进行同步)会优雅地打印从0到10的数字,3次在行中。为什么使用互斥体线程打印奇怪

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

using namespace std; 

struct A 
{ 
    void run() 
    {  
     lock_guard<mutex> l(mutex); 
     int i = 0; 
     while (i <= 10) 
      cout << "i = " << i++ << endl;    
    } 

    std::mutex mut; 
}; 

int main() 
{ 
    A a; 
    thread t1(&A::run, &a); 
    thread t2(&A::run, &a); 
    thread t3(&A::run, &a); 

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

但由于某种原因,输出的开始总是或多或少被破坏。其余的有时可以,有时不可以。显然,它不是同步的。这很奇怪,因为我期望当一个线程获得对互斥锁的锁定时,其他线程将不会进入run方法的主体。但显然这不是事实。

i = i = 00i = 0 

i = 1 
i = 2 
i = 3 

有人能解释这一点吗?

我想也许我需要围绕std :: ref的线程构造函数中的'a'对象,但不,这没有帮助。

+3

有些人在遇到问题时会想:“我知道,我会用线程”,然后他们有两个有错误的地方。 – Slava

+6

'lock_guard l(mutex);'和'std :: mutex mut;'? –

+0

没有帮助,但我发现锁的使用没有引人注目。制作具有锁定防护的打印方法,并从循环内调用打印方法。锁应尽可能最小化阻塞。当您只需要保护打印时,没有任何阻碍所有“运行”方法的要点。 – Kieveli

回答

7

此:

lock_guard<mutex> l(mutex); 

声明类型lock_guard<mutex>接受std::mutex参数这可能不是你想要的功能l。由于没有正确使用包装,所以发生竞态条件。要声明std::lock_guard类型的变量,并用mut成员变量,而不是将其初始化:

std::lock_guard<std::mutex> l(mut); 

为了避免这种困惑不使用using namespace std;

+2

在最烦人的解析中有趣的转折。 – Persixty

0

这是错误的引用:'应该lock_guard l(mut);'但仍然试试这个:

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

using namespace std; 

struct A 
{ 
    void run() 
    {  
     int i = 0; 
     while (i <= 10) 
      printOutput(i); 
    } 

    void printOutput(int i) 
    { 
     lock_guard<mutex> l(mut); 
     cout << "i = " << i++ << endl;    
    } 

    std::mutex mut; 
}; 

int main() 
{ 
    A a; 
    thread t1(&A::run, &a); 
    thread t2(&A::run, &a); 
    thread t3(&A::run, &a); 

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

我想你会发现输出更有趣...有时。尝试使用调试编译并进行优化 - 它们可能会有不同的表现。

1

答案是我犯了一个错字。我没有写'mut',而是输入'mutex'。代码令人惊讶地编译,但如注释中所述,这样我就声明了一个函数,而不是锁定互斥锁('最令人头痛的解析')。这就是为什么'run'方法的代码在线程之间不同步的原因。

正如在评论中指出的,也不使用'using namespace std'会阻止我出现这个错误,因为我很快就会注意到我没有成员变量'std :: mutex',但'mut ”。