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

回到顶部