≡菜单

如何在C程序中终止线程(pthread_exit示例)

在Linux线程系列的第二部分(线程创建和标识)中,我们讨论了线程ID,如何比较两个线程ID以及如何创建线程。

在本文中,我们将主要关注线程的终止方式。

Linux线程系列: part 1, part 2,第3部分(本文)。

C线程示例程序

如果我们采用本系列第二部分中讨论的相同示例:

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

pthread_t tid[2];

void* doSomeThing(void *arg)
{
    unsigned long i = 0;
    pthread_t id = pthread_self();

    if(pthread_equal(id,tid[0]))
    {
        printf("\n First thread processing\n");
    }
    else
    {
        printf("\n 第二线程处理\n");
    }

    for(i=0; i<(0xFFFFFFFF);i++);

    return NULL;
}

int main(void)
{
    int i = 0;
    int err;

    while(i < 2)
    {
        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
        if (err != 0)
            printf("\ncan't create thread :[%s]", strerror(err));
        else
            printf("\n Thread created successfully\n");

        i++;
    }

    sleep(5);
    return 0;
}

你有没有观察到‘sleep()’ function being used? Did you get a question about why 睡觉() is being used? Well if you did then you are at the correct place to get the answer and if you did not then also its going to be a good read ahead.

If I remove the 睡觉() function from the code above and then try to compile and run it, I see the following output :

$ ./threads
Thread created successfully
First thread processing
Thread created successfully

But if I run it with 睡觉() enabled then I see the output as  :

$ ./threads
Thread created successfully
First thread processing
Thread created successfully
第二线程处理

所以我们看到了日志‘第二线程处理’ is missing in case we remove the 睡觉() function.

那么,为什么会这样呢?好吧,发生这种情况是因为在即将调度第二个线程之前,父线程(从中创建了两个线程)已完成其执行。这意味着运行main()函数的默认线程已完成,因此该进程终止为main()返回。

线程终止

如上所述,每个程序都以至少一个线程开始,该线程是在其中执行main()函数的线程。因此,程序中执行的每个线程的最大生存期是主线程的生存期。因此,如果我们希望主线程应该等到所有其他线程都结束,则可以使用函数pthread_join()。

#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);

上面的函数确保其父线程在完成之前不会终止。从父线程内部调用此函数,第一个参数是要等待的线程的线程ID,第二个参数是我们要父线程等待的线程的返回值。如果我们对返回值不感兴趣,则可以将此指针设置为NULL。

如果我们进行更广泛的分类,则可以看到线程可以通过三种方式终止:

  1. 如果线程从其启动例程返回。
  2. 如果被其他线程取消。这里使用的函数是pthread_cancel()。
  3. 如果从内部调用pthread_exit()函数。

这里的重点将放在pthread_exit()上。其原型如下:

#include <pthread.h>
void pthread_exit(void *rval_ptr);

因此,我们看到此函数仅接受一个参数,这是调用此函数的线程的返回值。此返回值由正在等待该线程终止的父线程访问。由pthread_exit()函数终止的线程的返回值可在上面刚刚说明的pthread_join的第二个参数中访问。

C线程终止示例

让我们举一个使用上面讨论的功能的例子:

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

pthread_t tid[2];
int ret1,ret2;

void* doSomeThing(void *arg)
{
    unsigned long i = 0;
    pthread_t id = pthread_self();

    for(i=0; i<(0xFFFFFFFF);i++);

    if(pthread_equal(id,tid[0]))
    {
        printf("\n First thread processing done\n");
        ret1  = 100;
        pthread_exit(&ret1);
    }
    else
    {
        printf("\n 第二线程处理 done\n");
        ret2  = 200;
        pthread_exit(&ret2);
    }

    return NULL;
}

int main(void)
{
    int i = 0;  
    int err;
    int *ptr[2];

    while(i < 2)
    {
        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
        if (err != 0)
            printf("\ncan't create thread :[%s]", strerror(err));
        else
            printf("\n Thread created successfully\n");

        i++;
    }

    pthread_join(tid [0],(void **)&(ptr[0]));
    pthread_join(tid [1],(void **)&(ptr[1]));

    printf("\n return value from first thread is [%d]\n", *ptr[0]);
    printf("\n return value from second thread is [%d]\n", *ptr[1]);

    return 0;
}

在上面的代码中:

  • 我们使用pthread_create()创建了两个线程
  • 两个线程的启动函数相同,即doSomeThing()
  • 线程使用带有返回值的pthread_exit()函数从start函数退出。
  • 创建线程之后,在主函数中,调用pthread_join()函数以等待两个线程完成。
  • 两个线程完成后,它们的返回值将由pthread_join()调用中的第二个参数访问。

上面代码的输出为:

$ ./threads
Thread created successfully
Thread created successfully
First thread processing done
第二线程处理 done
return value from first thread is [100]
return value from second thread is [200]

因此,我们看到两个线程都完全执行,并且在主函数中访问了它们的返回值。

如果您喜欢这篇文章,您可能还会喜欢..

  1. 50个Linux Sysadmin教程
  2. 50个最常用的Linux命令(包括示例)
  3. 排名前25位的最佳Linux性能监视和调试工具
  4. 妈妈,我找到了! – 15个实用的Linux Find命令示例
  5. Linux 101 Hacks第二版电子书 Linux 101黑客手册

Bash 101 Hacks书 Sed和Awk 101黑客手册 Nagios Core 3书 Vim 101黑客手册

{ 18 评论… 加一 }

  • 汤姆 2012年7月6日,上午7:48

    很棒的文章系列,确实帮助我以简洁的方式理解了线程。

  • 喜悦 2012年7月30日,上午12:43

    确实是一项很好的工作。它澄清了我对线程的一些疑问。

  • 希尔帕 2012年10月16日,上午5:13

    很好的文章。

  • vidya.v。 2012年10月22日,上午12:08

    对于初学者来说非常不错的文章。

  • rash 2012年10月26日,上午12:10

    好文章。 ..谢谢

  • 波斯拉 2013年2月11日,下午5:36

    有用。非常感谢!

  • 卡拉 2013年2月23日,上午1:34

    好文章。清楚地说明概念。谢谢

  • 比朱利 2013年3月6日,上午4:21

    很好的文章。很好的理解。

  • 沙克蒂·辛格 2013年3月21日,上午10:20

    在gcc编译器中复制以上程序后,它显示
    未定义的引用‘pthread_create’
    未定义的引用‘pthread_join’

  • 维维克 2013年3月22日,下午12:36

    在有关线程终止的示例中,main()中包含以下代码行

    pthread_join(tid [0],(void **)&(ptr[0])); // line 1
    pthread_join(tid [1],(void **)&(ptr[1])); // line 2

    那么主线程在执行第1行时是否立即开始等待?
    如果真是这样,将发生以下情况:

    1.主线程执行第1行,并开始等待第一个线程完成。
    2.同时,第二个线程完成(在第一个线程之前)并返回一些值。
    3.主线程尚未执行第2行(这意味着主线程尚未收到第二个线程的返回值)
    4.第一个线程完成,然后主线程可以执行第2行

    在那种情况下,我们会丢失(丢弃)在主线程pthread_join之前已经退出的第二个线程的返回值吗?

    – vivek

  • 阿米尔 2013年8月8日,下午12:39

    写得好,解释得好。 (尽管需要一些错字校正)
    它也有很好的例子

  • 尤尼斯 2013年8月31日,上午3:41

    这种解释是我完成作业所需要的。
    我到达这里,这是起点…
    非常感谢你。

  • 曼尼什 2015年4月16日,下午1:36

    我们需要将线程创建为JOINABLE,以便pthread_join起作用。

  • 普拉布 2015年8月27日,上午1:11

    我没有得到验证。但是很好的解释….Thanks dude..

  • 奇特拉G M 2016年4月6日,下午5:29

    漂亮的解释。非常感谢。

  • 奇特拉G M 2016年4月6日,下午5:41

    非常好的解释。

  • 南丫岛 2017年2月11日,晚上8:53

    非常好的文章,非常感谢

  • Aserrao 2017年4月18日,下午12:58

    非常好的文章!谢谢

发表评论