迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C语言 >

C 线程资源释放问题

作者:迹忆客 最近更新:2022/12/26 浏览次数:

C语言编程中很大的一个问题就是内存回收和资源释放的问题。在其他多数的高级语言中这些基本上都由底层来自动处理了。但是C语言是需要程序员自己来处理的。

线程的创建也是要占用资源的,当然这些资源是要随着线程的结束然后由操作系统回收的。 所以在多线程编程的过程中,不仅要关注并发和锁的问题,同时也要关心资源释放和回收的问题。 这是在C中避不开的一个事情。

在C中,线程有两种状态:结合态(joinable)分离态(detached)。 这两种状态的线程的资源释放方式是不同的。

  • 结合态: 线程退出后,使用pthread_cancel() 显示的结束线程,资源并不会释放。而是必须显示调用 pthread_join() 来释放资源。
  • 分离态: 线程退出后,自动释放并回收资源,不需要在调用pthread_join() 来回收资源。

严格来说,pthread_join() 并不是用来结束线程的(网上发现有的介绍说该函数是结束并回收资源,这是误导),而是等待线程结束之后来做清理工作的。 也就是说如果主线程创建了一个线程,这个线程一直运行,如果主线程想要终止这个线程,单纯使用pthread_join()是没法终止的,只能在这静静地等着线程结束。必须使用 pthread_cancel() 来终止,然后再使用 pthread_join() 来回收。

在最近做的一个PHP扩展的项目中,就使用到了线程。下面是部分代码

void *start_write_log()
{
    ...
    while(1){
        ...
    }
}

PHP_FUNCTION(jlog_start)
{
    if(server_start != 0) {
        return ;
    }
    server_start = 1;

    if (pthread_mutex_init(&mutex, NULL) != 0){
        // 互斥锁初始化失败
        php_error(E_ERROR,"互斥锁初始化失败\n");
    }

    var_node = PHP_USER_ALLOC(JLOG_VSG(node_size));

    int ret = pthread_create(&tid,NULL,start_write_log,NULL);
}
...
PHP_FUNCTION(jlog_stop)
{
    log_node *n;
    if(server_start == 0) {
        return ;
    }
    server_start = 0;
    while(!checkQueueEmpty() || !idle) {}

    pthread_cancel(tid);     // pthread_cancel() 只是用来结束线程,并不会回收线程的资源
    pthread_join(tid,NULL);  // pthread_join() 用来回收线程,释放其占用的资源。
    PHP_USER_FREE(var_node);
}

可以看到,使用pthread_cancel()结束线程,然后使用pthread_join()来释放资源。 最初的时候并没有考虑到线程资源释放和回收的问题,所以并没有使用pthread_join()。 所以当使用 valgrind 检测的时候出现一个错误:


==24257== 592 bytes in 1 blocks are possibly lost in loss record 65 of 67
==24257==    at 0x4C2C089: calloc (vg_replace_malloc.c:760)
==24257==    by 0x4012734: _dl_allocate_tls (in /usr/lib64/ld-2.17.so)
==24257==    by 0x7C1683B: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.17.so)
==24257==    by 0x8770AE: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:558)
==24257==    by 0x807BB7: execute_ex (zend_vm_execute.h:363)
==24257==    by 0x7D32CF: zend_execute_scripts (zend.c:1341)
==24257==    by 0x771BC1: php_execute_script (main.c:2613)
==24257==    by 0x878CFB: do_cli (php_cli.c:998)
==24257==    by 0x43572E: main (php_cli.c:1382)

...

==24257== LEAK SUMMARY:
==24257==    definitely lost: 0 bytes in 0 blocks
==24257==    indirectly lost: 0 bytes in 0 blocks
==24257==      possibly lost: 592 bytes in 1 blocks
==24257==    still reachable: 6,973 bytes in 73 blocks
==24257==         suppressed: 0 bytes in 0 blocks
==24257==
==24257== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

这就是说由pthread_create() 申请的资源最后没有释放回收。 加上 pthread_join()之后,问题随之解决了。

pthread_join() 方法除了用于回收资源之外,还有一个功能就是保证由主线程创建的线程一定可以执行完成。 如果没有pthread_join() 那么当主线程执行完成之后,那么创建的线程有可能还没来的及执行整个程序就退出了。 如果程序中没有其他方式的依赖关系来保证线程一定能执行,那么pthread_join() 也是必须的。 也就是说这个方法的作用是 等待线程执行完成然后释放回收资源。所以如果线程没有执行完,那么主线程就会阻塞到调用pthread_join()的地方等待线程执行完。

除了使用 pthread_join() 方法之外,上面我们也说过了,可以将线程创建为分离态。 这样就可以在线程结束后自动释放回收资源了。 体现在项目中的代码如下:

PHP_FUNCTION(jlog_start)
{
    pthread_attr_t attr;
    if(server_start != 0) {
        return ;
    }
    server_start = 1;

    pthread_attr_init(&attr);

    pthread_attr_setdetachstat(&attr,PTHREAD_CREATE_DETACHED);

    if (pthread_mutex_init(&mutex, NULL) != 0){
        // 互斥锁初始化失败
        php_error(E_ERROR,"互斥锁初始化失败\n");
    }

    var_node = PHP_USER_ALLOC(JLOG_VSG(node_size));

    int ret = pthread_create(&tid,NULL,start_write_log,NULL);
    /* 销毁一个目标结构,并且使它在重新初始化之前不能重新使用 */
    
    pthread_attr_destroy (&attr);
}

PHP_FUNCTION(jlog_stop)
{
    log_node *n;
    if(server_start == 0) {
        return ;
    }
    server_start = 0;
    while(!checkQueueEmpty() || !idle) {}

    pthread_cancel(tid);     // pthread_cancel() 只是用来结束线程,并不会回收线程的资源
    PHP_USER_FREE(var_node);
}

上面关于分离态的线程的介绍仅限于使用上,对于其中的数据结构不是这里的重点。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

Do you understand JavaScript closures?

发布时间:2025/02/21 浏览次数:108 分类:JavaScript

The function of a closure can be inferred from its name, suggesting that it is related to the concept of scope. A closure itself is a core concept in JavaScript, and being a core concept, it is naturally also a difficult one.

Do you know about the hidden traps in variables in JavaScript?

发布时间:2025/02/21 浏览次数:178 分类:JavaScript

Whether you're just starting to learn JavaScript or have been using it for a long time, I believe you'll encounter some traps related to JavaScript variable scope. The goal is to identify these traps before you fall into them, in order to av

How much do you know about the Prototype Chain?

发布时间:2025/02/21 浏览次数:150 分类:JavaScript

The prototype chain can be considered one of the core features of JavaScript, and certainly one of its more challenging aspects. If you've learned other object-oriented programming languages, you may find it somewhat confusing when you start

Vue - An In-Depth Guide to Lifecycle Hooks

发布时间:2025/02/21 浏览次数:117 分类:Vue

Vue has many lifecycle hooks, and it can be confusing to understand the meaning or purpose of each one. In this article, we will explain the function of each lifecycle hook and how to use them.

Solution for Flickering During Vue Template Parsing

发布时间:2025/02/21 浏览次数:103 分类:Vue

Solution for Flickering During Vue Template Parsing, Recently, while working on a project, I noticed that when the internet speed is slow, the screen flickers and the expression message appears. This happens because when the internet speed i

Pandas read_csv()函数

发布时间:2024/04/24 浏览次数:254 分类:Python

Pandas read_csv()函数将指定的逗号分隔值(csv)文件读取到 DataFrame 中。

Pandas 追加数据到 CSV 中

发布时间:2024/04/24 浏览次数:352 分类:Python

本教程演示了如何在追加模式下使用 to_csv()向现有的 CSV 文件添加数据。

Pandas loc vs iloc

发布时间:2024/04/24 浏览次数:837 分类:Python

本教程介绍了如何使用 Python 中的 loc 和 iloc 从 Pandas DataFrame 中过滤数据。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便