C 线程资源释放问题
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);
}
上面关于分离态的线程的介绍仅限于使用上,对于其中的数据结构不是这里的重点。
相关文章
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 loc vs iloc
发布时间:2024/04/24 浏览次数:837 分类:Python
-
本教程介绍了如何使用 Python 中的 loc 和 iloc 从 Pandas DataFrame 中过滤数据。
在 Pandas 中使用 stack() 和 unstack() 函数重塑 DataFrame
发布时间:2024/04/24 浏览次数:1289 分类:Python
-
本文讨论了 Pandas 中 stack() 和 unstack() 函数的使用。