为什么在杂注 omp 关键之后多次调用 printf 会产生乱码输出?

Why does multiple calls to printf following a pragma omp critical produce garbled output?

本文关键字:printf 输出 调用 omp 之后 为什么      更新时间:2023-10-16

我有两个程序生成多个线程并将hello <TID> world <TID>打印到标准输出。 第一个在一个函数中打印它:

#include <omp.h>                                                
#include <stdio.h>                                               
int main() {                               
#pragma omp parallel                       
{                                        
int ID = omp_get_thread_num();         
#pragma omp critical                                  
printf("hello  %d world %dn", ID, ID);                                  
}      
return 0;                                  
}    

二分之二:

#include <omp.h>                                                  
#include <stdio.h>                                           
int main() {                               
#pragma omp parallel                       
{                                        
int ID = omp_get_thread_num();         
#pragma omp critical                       
printf("hello  %d", ID);             
printf("world %dn", ID);                                                
}  
return 0;                                      
}      

使用单次调用printf()的程序,输出永远不会乱码。 但是,对于第二个程序,两次调用printf(),输出是乱码。 使用第二个程序,永远不会有干扰字符串序列的乱码;从来没有那种hhhhellloohello.... 但是,不同的输出字符串之间存在乱码;可能性的排列:

hello  27hello  62hello  52hello  50hello  10world 62...

举个例子。

我认为#pragma omp critical会断言在pragma之后相互排斥,直到并行化块结束。但事实似乎并非如此。#pragma omp critical是否仅适用于紧随其后的语句?

任何见解都会发生这种情况。 伊扎克

#pragma omp parallel一样,#pragma omp critical仅限于下一行,除非你也把它做成一个块。

如果不希望交错输出,请使用另一组大括号:

#pragma omp critical                       
{
printf("hello  %d", ID);             
printf("world %dn", ID); 
}

可能是因为 POSIX 要求单个 I/O 函数调用是线程安全的,但不需要在单独的函数调用之间锁定。 规范的相关部分很好地隐藏在有关以下内容的页面上:

  • flockfile()
  • funlockfile()
  • ftrylockfile()

规范的相关部分接近末尾:

所有引用(FILE *(对象的函数,除了那些名称以_unlocked结尾的函数,都应该表现得好像它们在内部使用flockfile()funlockfile()来获得这些(FILE *(对象的所有权。

这意味着,例如,printf()必须在进入时执行(相当于(flockfile(stdout),在退出时执行(相当于(funlockfile(stdout)

这与您在 OMP 代码中看到的内容完全匹配。 对printf()的单个调用的文本不会与任何其他并发调用交错,但printf()调用的顺序在线程之间交错。

从这里,您可以看到一次应由单个线程运行的代码应放入代码块中。

添加一个名称也是一个好主意,这样如果你使用多个这样的指令,它们就不会冲突,