#include <stdio.h>
#include <pthread.h>
void *thread_func(void *arg)
{
printf("hello, world \n");
return 0;
}
int main(void)
{
pthread_t t1, t2;
pthread_create(&t1, NULL, thread_func, NULL);
pthread_create(&t2, NULL, thread_func, NULL);
printf("t1 = %d\n",t1);
printf("t2 = %d\n",t2);
return 0;
}
上面的程序创建了两个线程,每个线程打印&#34; Hello World&#34;。
所以,根据我的理解,&#34; Hello world&#34;应该打印最多2次。
但是,多次执行同一个程序(背对背)时,有些情况会出现&#34; Hello world&#34;正在打印更多而不是 2 次。所以我不清楚如何打印出意外的次数?
以下是示例输出:
[rr@ar ~]$ ./a.out
t1 = 1290651392
t2 = 1282258688
hello, world
hello, world
[rr@ar ~]$ ./a.out
t1 = 1530119936
t2 = 1521727232
hello, world
hello, world
hello, world
如上所示,在多次执行程序后,&#34;你好,世界&#34; 打印 3 次。任何人都可以建议如何打印3次?
答案 0 :(得分:3)
您遇到了线程安全问题。我在Linux 16.04中运行了多次代码,它产生了许多不同的输出,而3 hello world
消息的输出很少存在。更频繁地,根本没有输出,这意味着main
比能够完成输出的线程更快地终止。有时会产生部分输出,如:
t1=xxxx
t2=yyyy
he
这意味着main
正在退出,而只有一个线程能够在stdout缓冲区中推送一些字符。请记住,main
的正常返回相当于调用exit
来刷新stdio缓冲区。
虽然我无法真正理解当你观察3条消息时幕后发生的事情,但我怀疑存在一个运行竞赛让main
刷新当前正被其中一个线程冲洗的缓冲区。如果不仔细检查printf
的源代码,就很难说更多。一个可能的(粗略)场景看起来像:
hello world
hello world
hello world
printf
未被定义为线程安全的,这意味着实现者可能会这样或不实现它(可能在大多数情况下不是这样)。因此,与使用某些共享资源的任何函数一样,您需要一些互斥锁以防止缓冲区并发等。
在你的情况下,应该通过连接main中的线程来粗略地解决这个问题(3个输出),这将阻止线程终止前main
退出/刷新。但请注意,这不会解决其他并发问题(两个线程访问相同的缓冲区......)。
答案 1 :(得分:1)
好吧,让我展示一下这种情况会发生的情况。您可能知道(如果不这样,请阅读适当的手册页)printf()
是标准库不是线程安全的函数之一(pthread_<something>
中有一个列表,你可能也知道printf(3)
之前将其数据存储在一个缓冲区中,以发出write(2)
系统调用来实际将数据写入stdout。
printf()
调用,将缓慢的"Hello, world\n"
消息放入,并准备write(2)
,因为终端是 tty 设备而\n
结束输出字符串。printf(2)
数据(并使用"Hello, world\n"
的第二个副本填充相同的缓冲区),出于同样的原因,准备并完全执行{ {1}}整个缓冲区的syscall(现在包含两条消息)刷新缓冲区。这会使write(2)
出现两次。"Hello, world\n"
系统调用阻塞(两个线程无法同时对同一个inode进行write(2)
次调用 - 这是系统内核所保证的)刷新它的视图(可能存储在其堆栈中,并且仅包含对第一条消息的引用)缓冲区(在第一个write(2)
的末尾完成)并进行另一次写入还有一个"Hello, world\n"
消息)最终得分:终端上有三条"Hellow, world\n"
条消息。
最可能的事情是很难发生三条消息,因为你需要其中一个线程在时间内绕过另一条线程"Hellow, world\n"
决定是否需要在填充后刷新缓冲区(是一个很短的时间)然后首先进入阻塞printf
调用(如前所述,两个线程不能同时参与write(2)
调用同一个文件,这是不允许的内核)
答案 2 :(得分:0)
当主程序终止时,子线程也会终止。
可能会发生两个子线程在主任务完成之前执行。在这种情况下,您会看到两个“hello worlds”和问题中显示的输出。
也可能发生主程序在一个或两个线程打印输出之前完成。在这种情况下,您可以看到一个或没有“hello world”。
我认为这个程序的单次运行不会打印3次。我想你是在一个循环中执行程序,两个运行的输出混合在一起。添加:例如,想象一下以下场景:RUN1:打印两个数字,然后调度子线程并且每个打印一个“hello world”,然后RUN1 main被安排回来并且程序结束。接下来,RUN2启动。在这种情况下,两个子线程都是在主程序打印数字之前安排的。
所以你看到的输出如下:
t1=346236763 (RUN1 - main)
t2=876237623 (RUN1 - main)
hello, world (RUN1 - subthread)
hello, world (RUN1 - subthread)
hello, world (RUN2 - subthread)
hello, world (RUN2 - subthread)
t1=3786768623 (RUN2 - main)
t2=7843473478 (RUN2 - main)
输出可能被错误地解释为好像一次运行写了4个“hello worlds”。