【问题标题】:Why does this code output the same number multiple times?为什么这段代码多次输出相同的数字?
【发布时间】:2017-02-11 15:44:58
【问题描述】:

多次运行以下代码会产生相同数字多次出现的输出。我不确定为什么会这样。

#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

【问题讨论】:

  • 您是否考虑加入您的主题?
  • 你需要加入你的线程,你需要一个互斥锁或类似的来保护你的输出到 cout,目前是交错值。您可能还想考虑使用 std::thread。
  • 我不明白等待他们的终止会如何改变他们的输出
  • 确实加入解决了多值问题,但我不清楚为什么

标签: c++ multithreading pthreads


【解决方案1】:

这个程序的主要问题是:

  1. 从声明返回类型的函数的末尾掉出
  2. 不加入线程
  3. 在不同步的情况下从多个线程使用 std::cout

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

现在,问题的有趣部分

相同的数字出现不止一次。我不确定这是为什么。

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

【讨论】:

    【解决方案2】:

    Cubbi 在这里有正确的答案。真正的问题不是加入线程。剩下的留在这里供参考:

    您应该以不可预知的顺序看到输出,但它不应该产生重复的值。一般来说iostreams 不是线程安全的:

    来自 C++14:27.2.3 [iostreams.threadsafety]

    并发访问流对象(27.8、27.9)、流缓冲区对象(27.6)或 C 库流(27.9.2) 除非另有说明(27.4),否则多个线程可能会导致数据竞争(1.10)。 [注:数据竞赛 导致未定义的行为(1.10)。 ——尾注]

    但是 27.4 给出了适用于这种情况的例外情况:

    FILEs,对同步 (27.5.3.4) 标准 iostream 对象的格式化和未格式化输入的并发访问 不应导致多线程的 put (27.7.2.1) 和 output (27.7.3.1) 函数或标准 C 流 在数据竞赛(1.10)中。 [注意:用户仍然必须通过以下方式同步这些对象和流的并发使用 如果他们希望避免交错字符,则可以使用多个线程。 ——尾注]

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

    【讨论】:

    • 这并不完全准确。如果您希望打印任何可理解的内容,您确实需要同步您的输出,但标准说从多个线程访问像 cout 这样的对象是安全的,即使没有显式同步也是如此。所以这里没有UB,至少就执行输出而言。
    • @NeilButterworth:我已经添加了相关的标准报价。
    • 我无法访问这台机器上的标准,但是 27.4 说了什么?
    • 他使用std::cout 没有禁用同步,这是线程安全的输出,在相同的标准。它只是不能保证有意义的输出,也不能防止终止问题。
    • @NeilButterworth:好点。那么可能是实现中的一个错误。
    【解决方案3】:

    在多线程中,不能保证一个线程中的代码将在另一个线程中的代码之前或之后运行,而两者之间没有任何同步机制。

    你有一个主线程和其他由主线程创建的线程,你不能指望数字'i'不会在主线程的for循环增加它之前被计算出来。

    【讨论】:

      猜你喜欢
      • 2023-02-17
      • 1970-01-01
      • 1970-01-01
      • 2016-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多