第1部分 C++多线程系统编程 1
第1章 线程安全的对象生命期管理 3
1.1当析构函数遇到多线程 3
1.1.1线程安全的定义 4
1.1.2 MutexLock与MutexLockGuard 4
1.1.3一个线程安全的Counter示例 4
1.2对象的创建很简单 5
1.3销毁太难 7
1.3.1 mutex不是办法 7
1.3.2作为数据成员的mutex不能保护析构 8
1.4线程安全的Observer有多难 8
1.5原始指针有何不妥 11
1.6神器shared_ptr/weak_ptr 13
1.7插曲:系统地避免各种指针错误 14
1.8应用到Observer上 16
1.9再论shared_ptr的线程安全 17
1.10 shared_ptr技术与陷阱 19
1.11对象池 21
1.11.1 enable_shared_from_this 23
1.11.2弱回调 24
1.12替代方案 26
1.13心得与小结 26
1.14 Observer之谬 28
第2章 线程同步精要 31
2.1互斥器(mutex) 32
2.1.1只使用非递归的mutex 33
2.1.2死锁 35
2.2条件变量(condition variable) 40
2.3不要用读写锁和信号量 43
2.4封装MutexLock、MutexLockGuard、Condition 44
2.5线程安全的Singleton实现 48
2.6 sleep(3)不是同步原语 50
2.7归纳与总结 51
2.8借shared_ptr实现copy-on-write 52
第3章 多线程服务器的适用场合与常用编程模型 59
3.1进程与线程 59
3.2单线程服务器的常用编程模型 61
3.3多线程服务器的常用编程模型 62
3.3.1 one loop per thread 62
3.3.2线程池 63
3.3.3推荐模式 64
3.4进程间通信只用TCP 65
3.5多线程服务器的适用场合 67
3.5.1必须用单线程的场合 69
3.5.2单线程程序的优缺点 70
3.5.3适用多线程程序的场景 71
3.6“多线程服务器的适用场合”例释与答疑 74
第4章 C++多线程系统编程精要 83
4.1基本线程原语的选用 84
4.2 C/C++系统库的线程安全性 85
4.3 Linux上的线程标识 89
4.4线程的创建与销毁的守则 91
4.4.1 pthread_cancel与C++ 94
4.4.2 exit(3)在C++中不是线程安全的 94
4.5善用__thread关键字 96
4.6多线程与IO 98
4.7用RAII包装文件描述符 99
4.8 RAII与fork() 101
4.9多线程与fork() 102
4.10多线程与signal 103
4.11 Linux新增系统调用的启示 105
第5章 高效的多线程日志 107
5.1功能需求 109
5.2性能需求 112
5.3多线程异步日志 114
5.4其他方案 120
第2部分 muduo网络库 123
第6章 muduo网络库简介 125
6.1由来 125
6.2安装 127
6.3目录结构 129
6.3.1代码结构 131
6.3.2例子 134
6.3.3线程模型 135
6.4使用教程 136
6.4.1 TCP网络编程本质论 136
6.4.2 echo服务的实现 138
6.4.3七步实现finger服务 140
6.5性能评测 144
6.5.1 muduo与Boost.Asio、libevent2的吞吐量对比 145
6.5.2击鼓传花:对比muduo与libevent2的事件处理效率 148
6.5.3 muduo与Nginx的吞吐量对比 153
6.5.4 muduo与ZeroMQ的延迟对比 156
6.6详解muduo多线程模型 157
6.6.1数独求解服务器 157
6.6.2常见的并发网络服务程序设计方案 160
第7章 muduo编程示例 177
7.1五个简单TCP示例 178
7.2文件传输 185
7.3 Boost.Asio的聊天服务器 194
7.3.1 TCP分包 194
7.3.2消息格式 195
7.3.3编解码器LengthHeaderCodec 197
7.3.4服务端的实现 198
7.3.5客户端的实现 200
7.4 muduo Buffer类的设计与使用 204
7.4.1 muduo的IO模型 204
7.4.2为什么non-blocking网络编程中应用层buffer是必需的 205
7.4.3 Buffer的功能需求 207
7.4.4Buffer的数据结构 209
7.4.5 Buffer的操作 211
7.4.6其他设计方案 217
7.4.7性能是不是问题 218
7.5一种自动反射消息类型的Google Protobuf网络传输方案 220
7.5.1网络编程中使用Protobuf的两个先决条件 220
7.5.2根据type name反射自动创建Message对象 221
7.5.3 Protobuf传输格式 226
7.6在muduo中实现Protobuf编解码器与消息分发器 228
7.6.1什么是编解码器(codec) 229
7.6.2实现ProtobufCodec 232
7.6.3消息分发器(dispatcher)有什么用 232
7.6.4 ProtobufCodec与ProtobufDispatcher的综合运用 233
7.6.5 ProtobufDispatcher的两种实现 234
7.6.6 ProtobufCodec和ProtobufDispatcher有何意义 236
7.7限制服务器的最大并发连接数 237
7.7.1为什么要限制并发连接数 237
7.7.2在muduo中限制并发连接数 238
7.8定时器 240
7.8.1程序中的时间 240
7.8.2 Linux时间函数 241
7.8.3 muduo的定时器接口 242
7.8.4 Boost.Asio Timer示例 243
7.8.5 Java Netty示例 245
7.9测量两台机器的网络延迟和时间差 248
7.10用timing wheel踢掉空闲连接 250
7.10.1 timing wheel原理 251
7.10.2代码实现与改进 254
7.11简单的消息广播服务 257
7.12“串并转换”连接服务器及其自动化测试 260
7.13 socks4a代理服务器 264
7.13.1 TCP中继器 264
7.13.2 socks4a代理服务器 267
7.13.3 N:1与1:N连接转发 267
7.14短址服务 267
7.15与其他库集成 268
7.15.1 UDNS 270
7.15.2 c-ares DNS 272
7.15.3 curl 273
7.15.4更多 275
第8章 muduo网络库设计与实现 277
8.0什么都不做的EventLoop 277
8.1 Reactor的关键结构 280
8.1.1 Channel class 280
8.1.2 Pollerclass 283
8.1.3 EventLoop的改动 287
8.2TimerQueue定时器 290
8.2.1 TimerQueue class 290
8.2.2 EventLoop的改动 292
8.3 EventLoop::runInLoop()函数 293
8.3.1提高TimerQueue的线程安全性 296
8.3.2 EventLoopThread class 297
8.4实现TCP网络库 299
8.5TcpServer接受新连接 303
8.5.1 TcpServer class 304
8.5.2 TcpConnection class 305
8.6TcpConnection断开连接 308
8.7 Buffer读取数据 313
8.7.1 TcpConnection使用Buffer作为输入缓冲 314
8.7.2 Buffer::readFd() 315
8.8 TcpConnection发送数据 316
8.9完善TcpConnection 320
8.9.1 SIGPIPE 321
8.9.2 TCP No Delay和TCP keepalive 321
8.9.3 WriteCompleteCallback和HighWaterMarkCallback 322
8.10多线程TcpServer 324
8.11 Connector 327
8.12 TcpClient 332
8.13 epoll 333
8.14测试程序一览 336
第3部分 工程实践经验谈 337
第9章 分布式系统工程实践 339
9.1我们在技术浪潮中的位置 341
9.1.1分布式系统的本质困难 343
9.1.2分布式系统是个险恶的问题 344
9.2分布式系统的可靠性浅说 349
9.2.1分布式系统的软件不要求7x24可靠 352
9.2.2“能随时重启进程”作为程序设计目标 354
9.3分布式系统中心跳协议的设计 356
9.4分布式系统中的进程标识 360
9.4.1错误做法 361
9.4.2正确做法 362
9.4.3 TCP协议的启示 363
9.5构建易于维护的分布式程序 364
9.6为系统演化做准备 367
9.6.1可扩展的消息格式 368
9.6.2反面教材:ICE的消息打包格式 369
9.7分布式程序的自动化回归测试 370
9.7.1单元测试的能与不能 370
9.7.2分布式系统测试的要点 373
9.7.3分布式系统的抽象观点 374
9.7.4一种自动化的回归测试方案 375
9.7.5其他用处 379
9.8分布式系统部署、监控与进程管理的几重境界 380
9.8.1境界1:全手工操作 382
9.8.2境界2:使用零散的自动化脚本和第三方组件 383
9.8.3境界3:自制机群管理系统,集中化配置 386
9.8.4境界4:机群管理与naming service结合 389
第10章 C++编译链接模型精要 391
10.1 C语言的编译模型及其成因 394
10.1.1为什么C语言需要预处理 395
10.1.2 C语言的编译模型 398
10.2 C++的编译模型 399
10.2.1单遍编译 399
10.2.2前向声明 402
10.3 C++链接(linking) 404
10.3.1函数重载 406
10.3.2 inline函数 407
10.3.3模板 409
10.3.4虚函数 414
10.4工程项目中头文件的使用规则 415
10.4.1头文件的害处 416
10.4.2头文件的使用规则 417
10.5工程项目中库文件的组织原则 418
10.5.1动态库是有害的 423
10.5.2静态库也好不到哪儿去 424
10.5.3源码编译是王道 428
第11章 反思C++面向对象与虚函数 429
11.1朴实的C++设计 429
11.2程序库的二进制兼容性 431
11.2.1什么是二进制兼容性 432
11.2.2有哪些情况会破坏库的ABI 433
11.2.3哪些做法多半是安全的 435
11.2.4反面教材:COM 435
11.2.5解决办法 436
11.3避免使用虚函数作为库的接口 436
11.3.1 C++程序库的作者的生存环境 437
11.3.2虚函数作为库的接口的两大用途 438
11.3.3虚函数作为接口的弊端 439
11.3.4假如Linux系统调用以COM接口方式实现 442
11.3.5 Java是如何应对的 443
11.4动态库接口的推荐做法 443
11.5以boost::function和boost::bind取代虚函数 447
11.5.1基本用途 450
11.5.2对程序库的影响 451
11.5.3对面向对象程序设计的影响 453
11.6 iostream的用途与局限 457
11.6.1 stdio格式化输入输出的缺点 457
11.6.2 iostream的设计初衷 461
11.6.3 iostream与标准库其他组件的交互 463
11.6.4 iostream在使用方面的缺点 464
11.6.5 iostream在设计方面的缺点 468
11.6.6一个300行的memory buffer output stream 476
11.6.7现实的C++程序如何做文件IO 480
11.7值语义与数据抽象 482
11.7.1什么是值语义 482
11.7.2值语义与生命期 483
11.7.3值语义与标准库 488
11.7.4值语义与C++语言 488
11.7.5什么是数据抽象 490
11.7.6数据抽象所需的语言设施 493
11.7.7数据抽象的例子 495
第12章 C++经验谈 501
12.1用异或来交换变量是错误的 501
12.1.1编译器会分别生成什么代码 503
12.1.2为什么短的代码不一定快 505
12.2不要重载全局::operator new() 507
12.2.1内存管理的基本要求 507
12.2.2重载::operator new()的理由 508
12.2.3::operator new()的两种重载方式 508
12.2.4现实的开发环境 509
12.2.5重载::operator new()的困境 510
12.2.6解决办法:替换malloc() 512
12.2.7为单独的class重载::operator new()有问题吗 513
12.2.8有必要自行定制内存分配器吗 513
12.3带符号整数的除法与余数 514
12.3.1语言标准怎么说 515
12.3.2 C/C++编译器的表现 516
12.3.3其他语言的规定 516
12.3.4脚本语言解释器代码 517
12.3.5硬件实现 521
12.4在单元测试中mock系统调用 522
12.4.1系统函数的依赖注入 522
12.4.2链接期垫片(link seam) 524
12.5慎用匿名namespace 526
12.5.1 C语言的static关键字的两种用法 526
12.5.2 C++语言的static关键字的四种用法 526
12.5.3匿名namespace的不利之处 527
12.5.4替代办法 529
12.6采用有利于版本管理的代码格式 529
12.6.1对diff友好的代码格式 530
12.6.2对grep友好的代码风格 537
12.6.3一切为了效率 538
12.7再探std::string 539
12.7.1直接拷贝(eager copy) 540
12.7.2写时复制(copy-on-write) 542
12.7.3短字符串优化(SSO) 543
12.8用STL algorithm轻松解决几道算法面试题 546
12.8.1用next_permutation()生成排列与组合 546
12.8.2用unique()去除连续重复空白 548
12.8.3用{make,push,pop﹜_heap()实现多路归并 549
12.8.4用partition()实现“重排数组,让奇数位于偶数前面” 553
12.8.5用lower_bound()查找IP地址所属的城市 554
第4部分 附录 559
附录A 谈一谈网络编程学习经验 561
附录B 从《C++ Primer(第4版)》入手学习C++ 579
附录C 关于Boost的看法 591
附录D 关于TCP并发连接的几个思考题与试验 593
参考文献 599