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;}
运行结果如下:
- muduo网络库学习笔记(3):Thread类
- muduo网络库学习笔记(1):Timestamp类
- muduo网络库学习笔记(12):TcpServer和TcpConnection类
- muduo网络库学习之Exception类、Thread 类封装中的知识点(重点讲pthread_atfork())
- muduo库Thread类剖析
- muduo 4 网络库学习之Exception类、Thread 类封装中的知识点(重点讲pthread_atfork())
- muduo网络库学习笔记(2):原子性操作
- muduo网络库学习笔记(4):互斥量和条件变量
- muduo网络库学习笔记(5):线程池的实现
- muduo网络库学习笔记(6):单例类(线程安全的)
- muduo网络库学习笔记(7):线程特定数据
- muduo网络库学习笔记(10):定时器的实现
- muduo网络库学习笔记(8):高效日志类的封装
- muduo::Thread类分析
- muduo网络库学习之muduo_http 库涉及到的类
- muduo网络库学习之muduo_inspect 库涉及到的类
- muduo网络库学习之muduo_inspect 库涉及到的类
- 【muduo网络库学习】之Acceptor类分析
- RN备忘
- java中数组的三种定义方式
- Android Fragment完全解析
- css+html对带引号的函数传参
- POJ 2485 Highways 最小生成树
- muduo网络库学习笔记(3):Thread类
- 169.You have two tables with referential integrity enforced between them. You need to insert data to
- Ubuntu apt 本地源 离线安装
- Mac上更新Ruby,安装cocoapods详细教程
- ThreeJS 开发实例
- 插入排序与归并排序
- 抽象类
- JAVA 万年历(阳历+阴历+节日+时间)
- Javascript中this关键字详解