线程创建期间程序执行的流程

时间:2014-08-04 11:46:09

标签: c multithreading pthreads

我是线程新手。

我已经编写了一个示例程序来创建一个线程。

#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
#include<string.h>
#include<pthread.h>


void * func(void * temp)
{
    printf("inside function\n");
    return NULL;
}

int main()
{
    pthread_t pt1;
    printf("creating thread\n");
    pthread_create(&pt1,NULL,&func,NULL);
    printf("inside main created thread\n");

return 0;
}

编译后,答案是:

creating thread
inside main created thread
inside function
inside function

我理解答案可能会有所不同,因为在执行func return 0;之前可能会调用printf。 但是如何在解决方案中,inside function被打印两次?

使用gcc -o temp thread1.c -lpthread进行编译 在第一次运行:

creating thread
inside main created thread
第二轮

creating thread
inside main created thread
inside function
inside function

使用gcc -pthread -o temp thread1.c进行编译 在第一次运行时:

creating thread
inside main created thread
inside function
inside function

我在

上观察到了这种行为
gcc version: 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
Kernel release:2.6.32-24-generic
glib version:2.11.1

4 个答案:

答案 0 :(得分:3)

我在 gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5),glib版本2.15 以及O2标志上观察到此问题。如果没有任何优化标记,则不会出现此问题。

为什么输出很奇怪
C语言规范不引用任何特定的编译器,操作系统或CPU。它引用了一个抽象机器,它是实际系统的概括。这个抽象机器(至少达到C99规格)是单线程的。因此,默认情况下,标准库(包括printf)不需要是线程安全的。如果您在线程中使用标准库函数(使用某些库,例如posix libpthread),则您需要在访问非参数标准库函数之前添加同步(互斥,信号量,condvar等)。如果你不这样做,可能会不时出现令人惊讶的结果,你应该自担风险。

在我可以重现此问题的环境中进行一些分析
分析为两个版本的标志生成的程序集,我找不到任何显着差异(Onething noticeable,printf s被转换为puts

查看puts

的来源
int
_IO_puts (str)
     const char *str;
{
  int result = EOF;
  _IO_size_t len = strlen (str);
  _IO_acquire_lock (_IO_stdout);

  if ((_IO_vtable_offset (_IO_stdout) != 0
       || _IO_fwide (_IO_stdout, -1) == -1)
      && _IO_sputn (_IO_stdout, str, len) == len
      && _IO_putc_unlocked ('\n', _IO_stdout) != EOF)
    result = MIN (INT_MAX, len + 1);

  _IO_release_lock (_IO_stdout);
  return result;
}

#ifdef weak_alias
weak_alias (_IO_puts, puts)
#endif

似乎问题在于_IO_putc_unlocked('\n', _IO_stdout)。这可能会刷新流并可能在更新流状态之前被杀死。

学习多线程编码
当主线程返回时,它终止整个进程。这包括所有其他线程。因此,请指示所有子线程退出(或使用pthread_kill)并使主线程退出pthread_exit或使用pthread_join

答案 1 :(得分:1)

首先,据我所知,使用“-pthread”进行编译相当于使用“-D_REENTRANT -lpthread”进行编译,因此这是唯一的区别。 请注意,printf等不可重入,因为它们在全局缓冲区上运行。

话虽如此,遗憾的是我无法重新创建问题的有趣的部分(你的printf在线程目标函数中似乎被调用了两次)。每种编译方法(-lpthread和-pthread)都给出了相同的结果:我从main内部获取打印,但没有从线程目标中打印(正如您在第一次运行时看到的那样)。我认为这只是一个时间问题,线程目标不是在主要退出之前“绕过”打印。实际上,在从main返回之前只是睡了1/100秒就可以获得线程目标函数的打印。试一试,让我们知道您的看法:

#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
#include<string.h>
#include<pthread.h>
#include <unistd.h>

void * func(void * temp)
{
    printf("inside function\n");
    return NULL;
}

int main()
{
    pthread_t pt1;
    printf("creating thread\n");
    pthread_create(&pt1,NULL,&func,NULL);
    printf("inside main created thread\n");

    /* ZzzzzZZZ... */
    usleep(10000);

    return 0;
}

我玩了延迟时间,即使是1/1000000秒:     usleep(1); 我仍然得到所有我期望的printfs。随着睡眠延迟的减少,打印更有可能发生故障,我希望看到这种情况。

关于多个打印:关于我之前的许多指出,printf等在全局结构上运行,并且可重入。如果你在printf之后刷新stdout,我有兴趣看看你的输出,所有这些都受到互斥锁的保护:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void * func(void * temp)
{
    pthread_mutex_lock(&mutex);
    printf("inside function\n");
    fflush(stdout);
    pthread_mutex_unlock(&mutex);

    return NULL;
}

int main()
{
    pthread_t pt1;

    pthread_mutex_lock(&mutex);
    printf("creating thread\n");
    fflush(stdout);
    pthread_mutex_unlock(&mutex);

    pthread_create(&pt1,NULL,&func,NULL);

    pthread_mutex_lock(&mutex);
    printf("inside main created thread\n");
    fflush(stdout);
    pthread_mutex_unlock(&mutex);

    usleep(10000);

    return 0;
}

修改

对不起,当我建议使用上面的fflush()时,我并不是100%清楚。我相信问题是你在字符被推到屏幕的时间和刷新缓冲区之间被打断了。当缓冲区实际上被真正刷新时,你已经有效地将字符串推了两次。

答案 2 :(得分:1)

通常,您不能假设printf和相关的内存结构是线程安全的。这取决于stdio库的实现方式。 特别是,当线程和进程终止时,可能会发生故障,因为运行时库通常会在退出之前刷新输出缓冲区。我已经看到过这种行为,解决方案通常是一个互斥或信号量来保护输出操作(更确切地说,是为了保护对FILE对象的访问)。

答案 3 :(得分:0)

我无法发表评论,但是several duplicate questions的答案更多。 glibc bug可能是导致意外行为的原因,并且如其他答案所述,建议使用pthread_exitpthread_join