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);
}
上面关于分离态的线程的介绍仅限于使用上,对于其中的数据结构不是这里的重点。
本文转载自:迹忆客(https://www.jiyik.com)
以上是 C 线程资源释放问题 的全部内容, 来源链接: utcz.com/z/290197.html