2017-02-11 49 views
2

多次运行以下代码会在同一个数字出现多次时产生输出。我不确定这是为什么。为什么此代码多次输出相同的数字?

#include <iostream> 
#include <pthread.h> 

using namespace std; 

const int NUM_THREADS = 5; 

void* thread_entry(void *i){ 
    cout<<(long)i<<endl; 
} 

int main() { 
    pthread_t threads[NUM_THREADS]; 

    long i; 
    for(i=0;i<NUM_THREADS;i++){ 
    pthread_create(&threads[i],NULL,&thread_entry,(void *)i); 
    } 

    return 0; 
} 

它编译g++ -std=c++11 main.cpp -lpthread

输出:

$ ./a.out 
0 
1 
4 
$ ./a.out 
014 
14 
23 
$ ./a.out 
02 
2 
3 
+0

您是否考虑加入您的主题? –

+0

你需要加入你的线程,并且你需要一个互斥体或类似的来保护你的输出到cout,而且目前是交错的值。你可能也想考虑使用std :: thread。 –

+0

我没有看到如何等待他们的终止会改变他们的输出 – Adam

回答

2

这个方案的主要问题是:

  1. 脱落的功能的端部与声明的返回类型
  2. 使用std ::法院从多个线程不接合的螺纹
  3. 不同步

3在技术上没有必要(如果您修复1和2),因为默认情况下,通过std :: cout输出是线程安全的,与数据竞争不同,但输出可以交错。

现在,对于这个问题

的有趣的部分相同数量的occures不止一次。我不确定这是为什么。

假设你正在使用Linux或类似的东西,当主函数存在,它执行exit其中,在GNU C运行时,执行__run_exit_handlers,然后调用_IO_cleanup_IO_cleanup的工作是写出所有未写入的输出缓冲区。如果发生这种情况其中一个其他线程正处于写入中间,例如在write(2)系统调用中,_IO_cleanup将查看缓冲区状态并看到它仍然“满”(_IO_file_write在线程中会更新缓冲区位置指示器,当它从write(2)返回时)。所以根据清理,缓冲区还没有被写入,并且它在同一个缓冲区上启动了自己的系统调用write(2)

1

Cubbi这里有正确的答案。真正的问题是不加入线程。我会离开这里,其余供参考:

现在你可以看到在一个不可预知的顺序输出,但它不应该产生重复的值。一般而言iostream s的不是线程安全:

从C++ 14:27.2.3 [iostreams.threadsafety]

并发访问一个流对象(27.8,27.9),流缓存器对象(27.6 )或C库流(27.9.2) 可能会导致数据竞争(1.10),除非另有规定(27.4)。 [注意:数据竞赛 导致未定义的行为(1.10)。 - 注完]

不过27.4给出了适用于这种情况例外:

文件,并发访问的同步(27.5.3.4)标准iostream对象的格式和无格式IN- 放( 27.7.2.1)和输出(27.7.3.1)函数或多线程的标准C流在数据争用(1.10)中不产生 。 [注意:如果用户希望避免交叉字符,用户必须仍然同步使用这些对象和多个线程。 - 尾注]

由于cout是同步的,因此应该是线程安全的。

+0

这并不严格准确。如果你期望得到任何可理解的打印,你确实需要同步你的输出,但是标准说,即使没有明确的同步,从多个线程访问像cout这样的对象也是安全的。所以这里没有UB,至少就执行输出而言。 –

+0

@NeilButterworth:我已经添加了相关的标准报价。 –

+0

我无法访问此机器上的标准,但27.4是什么意思? –

0

在多线程并不能保证在一个线程的代码会前或在另一个线程的代码不之间的任何同步机制后运行。

你有一个主线程和主线程创建其他线程,你不能指望“i”的数量不会前cout'ed 主线程的for循环增加它。

相关问题