进程有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 == ) {
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 == ) {
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() == ) {
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高效实现的更多相关文章

  1. 为什么要CGI

    1.微软为什么使用CGI? 微软曾经在不同场合极力推荐它的ASP技术,以取代CGI标准,这对微软当然是有利的,但是对一个网站来说ASP是不是一个明智的选择呢?这是一个值得大家深思熟虑的问题. 因为一旦 ...

  2. [笔记][Java7并发编程实战手冊]系列文件夹

    推荐学习多线程之前要看的书. [笔记][思维导图]读深入理解JAVA内存模型整理的思维导图文章里面的思维导图或则相应的书籍.去看一遍. 能理解为什么并发编程就会出现故障. Java7并发编程实战手冊 ...

  3. JAVA中的Fork/Join框架

    看了下Java Tutorials中的fork/join章节,整理下. 什么是fork/join框架 fork/join框架是ExecutorService接口的一个实现,可以帮助开发人员充分利用多核 ...

  4. getpid 与 gettid 与 pthread_self

    获取进程的PID(process ID) #include <unistd.h> pid_t getpid(void); 获取线程的TID(thread ID) 1)gettid或者类似g ...

  5. fork炸弹

    众所周知,bash是一款极其强大的shell,提供了强大的交互与编程功能.这样的一款shell中自然不会缺少“函数”这个元素来帮助程序进行 模块化的高效开发与管理.于是产生了由于其特殊的特性,bash ...

  6. Github上如何取消fork别人的repository

    在Github上如果看到有很不错的项目和作品,一般我们可以进行三种操作:那就是watch, star和fork. Watch也就是关注该repo的动态,star则类似于Facebook和Twitter ...

  7. 玩转nodeJS系列:使用原生API实现简单灵活高效的路由功能(支持nodeJs单机集群),nodeJS本就应该这样轻快

    前言: 使用nodeJS原生API实现快速灵活路由,方便与其他库/框架进行整合: 1.原生API,简洁高效的轻度封装,加速路由解析,nodeJS本就应该这样轻快 2.不包含任何第三方库/框架,可以灵活 ...

  8. 【java并发系列】Fork/Join任务(转)

    原文链接 当我们需要执行大量的小任务时,有经验的Java开发人员都会采用线程池来高效执行这些小任务.然而,有一种任务,例如,对超过1000万个元素的数组进行排序,这种任务本身可以并发执行,但如何拆解成 ...

  9. Java Fork/Join 框架

    简介 从JDK1.7开始,Java提供Fork/Join框架用于并行执行任务,它的思想就是讲一个大任务分割成若干小任务,最终汇总每个小任务的结果得到这个大任务的结果. 这种思想和MapReduce很像 ...

随机推荐

  1. [LeetCode OJ] Max Points on a Line—Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.

    //定义二维平面上的点struct Point { int x; int y; Point(, ):x(a),y(b){} }; bool operator==(const Point& le ...

  2. int, NSInteger, NSUInteger, NSNumber的区别

    新手在接触iOS或者Mac开发的时候,看到int和NSInteger,一般不清楚应该用哪个比较合适.我们先来查看下NSInteger的定义 #if __LP64__ || (TARGET_OS_EMB ...

  3. javascript关于原型的深刻理解

    Javascript继承机制的设计思想   作者: 阮一峰 日期: 2011年6月 5日 我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类&qu ...

  4. jquery mobile 主题

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  5. canvas 背景填充

    这儿介绍canvas的ccreatePattern函数, context.createPattern(Image,"repeat"),还可以repeat-x,reapter-y 还 ...

  6. struts1、ajax、jquery、json简单实例

    1.页面ajax代码,使用$.ajax,获得json对象后each $.ajax({ type:"GET", url:ctx + "/uploadImg.do" ...

  7. 转:Python 与 Excel 不得不说的事

    数据处理是 Python 的一大应用场景,而 Excel 则是最流行的数据处理软件.因此用 Python 进行数据相关的工作时,难免要和 Excel 打交道. 如果仅仅是要以表单形式保存数据,可以借助 ...

  8. bzoj3576: [Hnoi2014]江南乐

    Description 小A是一个名副其实的狂热的回合制游戏玩家.在获得了许多回合制游戏的世界级奖项之后,小A有一天突然想起了他小时候在江南玩过的一个回合制游戏.    游戏的规则是这样的,首先给定一 ...

  9. Spark笔记-treeReduce、reduce、reduceByKey

    参考资料: http://stackoverflow.com/questions/32281417/understadning-treereduce-in-spark http://stackover ...

  10. ASP.NET中Request.ApplicationPath、Request.FilePath、Request.Path、.Request.MapPath

    1.Request.ApplicationPath->当前应用的目录 2.Request.FilePath->对应于iis的虚拟目录   如 URL http://mockte.com/1 ...