草庐IT

Linux线程 | 创建 终止 回收 分离

Wayne_hzw 2023-03-28 原文

一、线程简介

  • 线程是参与系统调度的最小单位。它被包含在进程之中,是进程中的实际运行单位。

  • 一个进程中可以创建多个线程,多个线程实现并发运行,每个线程执行不同的任务。

  • 每个线程都有其对应的标识,称为线程 ID,线程 ID 使用 pthread_t 数据类型来表示。

二、线程的创建

线程是轻量级的并发执行单元,通过调用Linux系统提供的pthread库中的函数来创建和管理线程。

  • 包含头文件:
#include <pthread.h>
  • 定义线程函数:

线程函数是线程实际执行的函数,可以是任何可以被调用的函数。线程函数的原型如下:

void* thread_function(void* arg);

其中arg是传递给线程函数的参数,可以是任何类型的数据。线程函数的返回值为void*类型,可以返回任何类型的数据。

  • 创建线程:

创建线程需要调用pthread_create函数。该函数的原型如下:

int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
参数 类型 描述
thread pthread_t * 用于存储新线程标识符的指针
attr const pthread_attr_t * 用于指定新线程的属性,如栈大小、调度策略等,可以为 NULL,表示使用默认属性
start_routine void *(*)(void *) 新线程的起始函数,需要返回 void 指针类型的结果,并且带有一个 void 指针类型的参数
arg void * 传递给新线程起始函数的参数,可以为 NULL
返回值 int 0 表示成功,非 0 表示失败,错误代码保存在 errno

? 注意:在调用 pthread_create() 函数之后,新线程的执行与调用线程并行进行,它们之间没有特定的执行顺序。

下面是一个创建线程的例子:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,arg=%d, i=%d\n", *(int *)arg, i);
        sleep(1);
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t tid; // 线程标识符
    int arg = 123; // 传递给线程函数的参数

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, &arg) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 等待线程结束并回收资源
    if (pthread_join(tid, NULL) != 0) {
        printf("线程回收失败!\n");
        return 1;
    }

    printf("线程结束!\n");
    return 0;
}

三、 线程的终止

线程的终止有两种方式:自然终止强制终止

线程的自然终止是指线程执行完它的工作后自动退出,而强制终止是指在程序运行过程中,主线程或其他线程显式地终止一个正在运行的线程。

线程自然终止

线程可以通过调用pthread_exit函数来实现自然终止。pthread_exit函数的原型如下:

void pthread_exit(void *retval);

pthread_exit函数 无返回值,其中,参数retval是线程的退出状态,可以通过pthread_join函数获取。

下面是一个简单的例子,演示如何使用pthread_exit函数终止一个线程:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,i=%d\n", i);
        sleep(1);
    }
    pthread_exit((void *) "线程正常结束!");
}

int main()
{
    pthread_t tid; // 线程标识符

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, NULL) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 等待线程结束并回收资源
    void *retval;
    if (pthread_join(tid, &retval) != 0) {
        printf("线程回收失败!\n");
        return 1;
    }

    printf("%s\n", (char *)retval);
    printf("线程结束!\n");
    return 0;
}

上面的示例程序中,我们在线程函数中调用pthread_exit函数来终止线程,并返回一个字符串作为退出状态。

在主线程中,我们使用pthread_join函数等待线程结束,并通过指针retval获取线程的退出状态。

线程强制终止

在Linux中,线程的强制终止可以使用pthread_cancel函数来实现。pthread_cancel函数的原型如下:

int pthread_cancel(pthread_t thread);

其中,参数thread是要取消的线程标识符。当pthread_cancel函数被调用时,被取消的线程将立即退出。

参数 类型 描述
thread pthread_t 要取消的线程标识符
返回值 int 0 表示成功,非 0 表示失败,错误代码保存在 errno

?注意:调用 pthread_cancel() 函数只是向指定线程发送一个取消请求,让指定线程尽快退出执行,而不会立即终止它的执行。

线程在接收到取消请求后,可以通过调用 pthread_setcancelstate() pthread_setcanceltype() 函数来指定如何响应请求,这里不再展开说明。

下面是一个简单的例子,演示如何使用pthread_cancel函数强制终止一个线程:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,i=%d\n", i);
        sleep(1);
    }
    pthread_exit((void *) "线程正常结束!");
}

int main()
{
    pthread_t tid; // 线程标识符

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, NULL) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 等待一段时间后强制终止线程
    sleep(2);
    if (pthread_cancel(tid) != 0) {
        printf("线程取消失败!\n");
        return 1;
    }

    // 等待线程结束并回收资源
    void *retval;
    if (pthread_join(tid, &retval) != 0) {
        printf("线程回收失败!\n");
        return 1;
    }

    if (retval == PTHREAD_CANCELED) {
        printf("线程被取消!\n");
    } else {
        printf("%s\n", (char *)retval);
    }
    printf("线程结束!\n");
    return 0;
}

上面的示例程序中,主线程调用了pthread_cancel函数,强制终止了子线程。

在子线程函数中,我们使用pthread_exit函数返回了一个字符串,如果子线程正常结束,那么在主线程中打印出来的将是这个字符串;如果子线程被强制终止,那么在主线程中打印出来的将是线程被取消!

  • ?不是这个啦
  • 我是想说,前面好几次都提到了线程回收, 你是不是忘了告诉我了
  • ? 不好意思哈,一下子没忍住就说出来了

四、线程的回收

使用pthread_join函数等待线程结束。该函数需要两个参数:线程标识符和指向线程返回值的指针。

int pthread_join(pthread_t thread, void **value_ptr);
参数 类型 描述
thread pthread_t 要等待的线程标识符
value_ptr void ** 用于获取线程的退出状态的指针,可以为 NULL,表示不关心退出状态
返回值 int 0 表示成功,非 0 表示失败,错误代码保存在 errno

? 注意:调用 pthread_join() 函数会阻塞当前线程,直到指定的线程终止为止。

如果指定的线程已经终止,那么该函数会立即返回,并且不会阻塞。

另外,线程的退出状态只有在 pthread_join() 调用成功时才能被获取,否则 value_ptr 指向的值是未定义的。

如果线程终止后,其它线程没有调用 pthread_join()函数来回收该线程,这个线程会变成僵尸线程,会浪费系统资源;若僵尸线程积累过多,那么会导致应

用程序无法创建新的线程。

五、线程的分离

可以使用pthread_detach函数将线程分离。pthread_detach函数的原型如下:

int pthread_detach(pthread_t thread);
参数 类型 描述
thread pthread_t 要分离的线程标识符
返回值 int 0 表示成功,非 0 表示失败,错误代码保存在 errno 中

调用 pthread_detach() 函数将使得指定线程在退出时自动释放其相关资源,而不需要其他线程调用 pthread_join() 函数来等待它的退出并回收资源。

如果指定的线程已经被分离或者已经退出,那么调用 pthread_detach() 函数将返回一个错误。

下面是一个简单的例子,演示如何使用pthread_detach函数将线程分离:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,i=%d\n", i);
        sleep(1);
    }
    pthread_exit((void *) "线程正常结束!");
}

int main()
{
    pthread_t tid; // 线程标识符

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, NULL) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 分离线程
    if (pthread_detach(tid) != 0) {
        printf("线程分离失败!\n");
        return 1;
    }

    printf("线程已经分离,将自动回收资源!\n");

    // 程序结束
    return 0;
}

上面的示例程序中,我们在创建线程之后立即将线程分离,并打印一条提示信息,告诉用户线程已经分离,将在退出时自动回收资源。

当运行上面的程序时,可以看到如下输出:

线程已经分离,将自动回收资源!
这是线程函数,i=0
这是线程函数,i=1
这是线程函数,i=2
这是线程函数,i=3
这是线程函数,i=4

可以看到,程序创建了一个新线程,并立即将它分离。在子线程中,我们打印了5个字符串,每个字符串间隔1秒。

在主线程中,我们打印了一条提示信息,告诉用户线程已经分离,将在退出时自动回收资源。最后,程序正常结束,没有调用pthread_join函数。

小结

我们已经介绍了Linux线程的创建、终止、回收、分离等基本操作。

在实际编程中,我们可能还需要使用一些其他的函数和技巧,例如互斥锁、条件变量、信号量、读写锁等

? 欲知后事如何,请听下回分解!


?欢迎各位 ?点赞 ⭐收藏 ?评论,如有错误请留言指正,非常感谢!

以上,如果觉得对你有帮助,点个赞再走吧,这样@知微之见也有更新下去的动力!

也欢迎私信我,一起交流!

有关Linux线程 | 创建 终止 回收 分离的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  4. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  5. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  6. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  7. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  8. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  9. ruby - 使用多个数组创建计数 - 2

    我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

  10. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

随机推荐