muduo网络库学习笔记(3):Thread类

来源:互联网 发布:加拿大女王大学知乎 编辑:程序博客网 时间:2024/06/10 00:08

muduo网络库采用了基于对象的编程思想来封装线程类。

类图如下:
这里写图片描述
变量numCreated_表示创建的线程个数,类型为AtomicInt32,用到了我们上篇所说的原子性操作。
Thread类中还用到了CurrentThread类。

代码要点如下:

(1)线程标识符
Linux中,每个进程有一个pid,类型为pid_t,由getpid()取得。Linux下的POSIX线程也有一个id,类型为pthread_t,由pthread_self()取得,该id由线程库维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。Linux中的POSIX线程库实现的线程其实也是一个进程(LWP:轻量级进程),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。

有时候我们可能需要知道线程的真实pid。比如进程P1要向另外一个进程P2中的某个线程发送信号时,既不能使用P2的pid,更不能使用线程的pthread id,而只能使用该线程的真实pid,称为tid
函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。

return syscall(SYS_gettid)

因为使用系统调用开销很大,所以我们需要对所获取的tid做一个缓存,防止每次都使用系统调用,从而提高获取tid的效率。

代码片段:缓存tid文件名:CurrentThread.h......extern __thread int t_cachedTid;  // 线程真实pid(tid)的缓存......inline int tid(){    if (t_cachedTid == 0)    {      cacheTid();    }    return t_cachedTid;}
代码片段:cacheTid()文件名:Thread.ccvoid CurrentThread::cacheTid(){  if (t_cachedTid == 0)  {    t_cachedTid = detail::gettid();    int n = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);    // (void) n; 的用法是为了防止未使用变量n而出现编译错误    assert(n == 6); (void) n;  }}

(2)__thread关键字和POD类型
__thread是GCC内置的线程局部存储设施,存取效率可以和全局变量相比。__thread变量在每一个线程有一份独立实体,各个线程的值互不干扰。可以用来修饰那些带有全局性且值可能变,但是又不值得用全局变量保护的变量。用一个例子来理解它的用法。

#include <pthread.h>#include <iostream>#include <unistd.h>using namespace std;//__thread int var = 5;int var = 5;void *worker1(void* arg);void *worker2(void* arg);int main(){        pthread_t p1, p2;        pthread_create(&p1, NULL, worker1, NULL);        pthread_create(&p2, NULL, worker2, NULL);        pthread_join(p1, NULL);        pthread_join(p2, NULL);        return 0;}void *worker1(void* arg){        cout << ++var << endl;}void *worker2(void* arg){        cout << ++var << endl;}/** * 使用__thread关键字,输出为:  *                         6 *                         6 *  * 不使用__thread关键字,输出为: *                         6    *                         7 * /

**注:**__thread只能修饰POD类型,不能修饰class类型,因为无法自动调用构造函数和析构函数。

POD类型(plain old data)是指与C兼容的原始数据类型,例如,结构体和整型等C语言中的类型就是 POD 类型,但带有用户定义的构造函数或虚函数的类则不是:

__thread可以用于修饰全局变量、函数内的静态变量,但是不能用于修饰函数的局部变量或者class的普通成员变量。

另外,__thread变量的初始化只能用编译器常量。

__thread string t_obj1(“hello”);    // 错误,不能调用对象的构造函数__thread string* t_obj2 = new string;   // 错误,初始化必须用编译期常量__thread string* t_obj3 = NULL; // 正确,但是需要手工初始化并销毁对象

(3)pthread_atfork()函数

#include <pthread.h>int pthread_atfork(void (*prepare)(void),                     void (*parent)(void),                     void (*child)(void));

用法:调用fork时,内部创建子进程前在父进程中会调用prepare,内部创建子进程成功后,父进程会调用parent ,子进程会调用child。

(4)多线程与fork()
对于编写多线程程序来说,最好不要再调用fork(),即不要编写多线程多进程程序。因为Linux的fork()只克隆当前线程的thread of control ,不克隆其他线程。fork()之后,除了当前线程之外,其他线程都消失了,也就是说,不能一下子fork()出一个和父进程一样的多线程子进程。

fork()之后子进程中只有一个线程,其他线程都消失了,这就造成一个危险的局面。其他线程可能正好位于临界区之内,持有了某个锁,而它突然死亡,再也没有机会去解锁了。如果子进程试图再对同一个mutex加锁,就会立刻死锁。

一个在多线程程序里fork造成死锁的例子:

/* 死锁的原因: 1. 线程里的doit()先执行 2. doit执行的时候会给互斥量mutex加锁 3. mutex的内容会原样拷贝到fork出来的子进程中(在此之前,mutex变量的内容已经被线程改写成锁定状态) 4. 子进程再次调用doit的时候,在给互斥量mutex加锁的时候会发现它已经被加锁,所以就一直等待,直到拥有该互斥体的进程释放它(实际上没有人拥有这个mutex锁) 5. 线程的doit执行完成之前会把自己的mutex释放,但这时的mutex和子进程里的mutex已经是两份内存.所以即使释放了mutex锁也不会对子进程里的mutex造成什么影响*/#include <stdio.h>#include <time.h>#include <pthread.h>#include <unistd.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void* doit(void* arg){    printf("%d begin doit\n",static_cast<int>(getpid()));    pthread_mutex_lock(&mutex);    struct timespec ts = {2, 0};    nanosleep(&ts, NULL);    pthread_mutex_unlock(&mutex);    printf("%d end doit\n",static_cast<int>(getpid()));    return NULL;}int main(void){    printf("%d enter main\n", static_cast<int>(getpid()));    pthread_t tid;    pthread_create(&tid, NULL, doit, NULL);    struct timespec ts = {1, 0};    nanosleep(&ts, NULL);    if (fork() == 0)    {        doit(NULL);    }    pthread_join(tid, NULL);    printf("%d exit main\n",static_cast<int>(getpid()));    return 0;}

运行结果如下:
这里写图片描述

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 七个月宝宝脾胃不好怎么办 八个月宝宝脾虚怎么办 七个月宝宝脾胃虚怎么办 海岛奇兵点错了怎么办 螳螂的脚断了怎么办 海岛奇兵打不过玩家怎么办 海岛奇兵资源满了怎么办 海岛奇兵杯越来越多打不玩家怎么办 海岛奇兵控杯技巧 杯数太高怎么办 海岛奇兵发现求救信号怎么办 海岛奇兵被打了怎么办 小鱼翅卡喉咙了怎么办 鱼翅卡在喉咙里怎么办 斗鱼身份证被使用怎么办 做的鱼丸太腥了怎么办 做鱼丸太稀了怎么办 斗鱼手机号换了怎么办 斗鱼直播掉帧怎么办 手机一直卡顿点不动怎么办呢 斗鱼直播分值底怎么办 斗鱼6000鱼丸怎么办卡 斗鱼直播没人看怎么办 淘宝直播间没人气怎么办 挂水了还是有热度怎么办 陌陌工会不结算工资怎么办 滴滴给了差评怎么办 饿了么星级低怎么办 滴滴乘客给低星怎么办 蘑菇街自动收货前还没到怎么办 小主播人气太少别人看不到怎么办 税收分类编码不可用怎么办 斗鱼鱼翅充错了怎么办 苹果指纹摔坏了怎么办 小米5指纹坏了怎么办 苹果5s指纹失灵怎么办 学生赌博输了3万怎么办 电脑录屏没有声音怎么办 别人说你没有他美怎么办 没有你我怎么办歌词是什么意思 要是没有他我怎么办啊歌词 用喀秋莎保存的视频黑屏怎么办