码迷,mamicode.com
首页 > 其他好文 > 详细

fork安全的gettid高效实现

时间:2014-12-03 13:56:27      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:style   blog   io   ar   color   sp   for   div   问题   

进程有id,可以通过getpid()获得,线程也有id,但是glibc没有提供封装。需要自己发出系统调用。在关键路径,系统调用还是对性能有影响的。因此我们可以想到类似glibc对getpid做的cache化封装,用thread local的方式缓存每个线程的id,每个线程只有第一次调用gettid时才真正发起系统调用。

#include <stdio.h>
#include <syscall.h>
#include <unistd.h>

pid_t gettid() {
    static __thread pid_t cached_tid;
    if (cached_tid == 0) {
        cached_tid = syscall(SYS_gettid);
    }
    return cached_tid;
}

这段代码运行的很好,直到遇到fork。在我看来,fork是单线程时代的东西,与多线程格格不入,所以我们的代码中很少用到。其实这个问题除了对调用fork的线程来说是诡异的,因为fork时,其他线程是不会被fork的。但是对于主线程或者单线程程序,这个问题也还是存在的,存在就不爽。

或许可以想到用pthread_atfork来做这个事情,其原型是这样的。

int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));

文档说,child会在子进程中被调用。但是这里有个问题,cached_tid是线程局部变量,每个线程里的地址是不一样的,而child函数不支持传地址,难道我们要用动态生成代码(thunk)的猥琐方式?

其实不必那么复杂,虽然glibc缓存了getpid,但是fork后getpid是会变的,因此我们可以在每次调用时再多比较一下pid是否发生了变化:

pid_t gettid() {
    static __thread pid_t cached_pid;
    static __thread pid_t cached_tid;
    pid_t pid = getpid();
    if (cached_pid != pid || cached_tid == 0) {
        cached_pid == pid;
        cached_tid = syscall(SYS_gettid);
    }
    return cached_tid;
}

getpid是高效的,因此这段代码也是高校的。

查阅glibc的源代码,getpid是从当前线程控制块里读取的,其实里面也有线程id,但是glibc却迟迟不肯提供封装,让我想起了一句话,程序员何苦为难程序员。

测一下:

int main() {
    printf("Parent: pid=%d, tid=%d\n", getpid(), gettid());
    if (fork() == 0) {
        printf("Child: pid=%d, tid=%d\n", getpid(), gettid());
    } else {
        printf("Parent after fork: pid=%d, tid=%d\n", getpid(), gettid());
    }
}

Parent: pid=10776, tid=10776
Parent after fork: pid=10776, tid=10776
Child: pid=10777, tid=10777

行为符合预期。

fork安全的gettid高效实现

标签:style   blog   io   ar   color   sp   for   div   问题   

原文地址:http://www.cnblogs.com/chen3feng/p/4139730.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!