在C++开发中,如何根据应用场景选择使用线程或协程?请分别描述适合采用线程和协程的典型场景,并说明理由。
考察说明
考察对C++并发模型的理解,以及根据任务特性选择合适并发原语的能力。
回答思路
- 【回答框架 1】线程由操作系统调度,每个线程拥有独立的内核栈,上下文切换涉及内核态与用户态切换,开销较大。线程适合需要利用多核CPU进行并行计算、执行阻塞式I/O(如同步读写文件、网络请求)或调用第三方阻塞库的场景,因为线程可以独立阻塞而不影响其他线程。
- 【回答框架 2】协程是用户态调度,切换由程序控制,切换开销远小于线程,内存占用也小(每个协程栈通常较小)。C++20引入的std::coroutine定义了协程框架,但标准库未提供调度器,常用第三方库如libco、Boost.Context等。协程适合高并发、I/O密集型且等待时不占用CPU的场景,比如大量网络连接下的异步I/O、事件循环模型。
- 【回答框架 3】选择依据:若任务是CPU密集型的并行计算,且需要充分利用多核,则选择线程,配合并行算法或std::thread;若任务是I/O密集型的,存在大量等待且需要高并发连接,则协程或异步I/O更优。注意线程适用性广,协程更适合I/O密集和用户态切换频繁的场景。
- 【回答框架 4】C++中协程不直接提供并行性,只提供并发性(即在一个线程上交错执行),而线程可实现真正的并行。因此,若需要同时执行多个CPU密集任务,应使用线程;若需要同时处理大量连接且每个连接的操作大部分是等待,则使用协程。现代设计常将两者结合:用少量线程运行事件循环,在事件循环中使用协程处理I/O。
- 【关键点 1】线程适合CPU密集和阻塞式I/O场景,可并行利用多核。
- 【关键点 2】协程适合I/O密集和高并发场景,切换开销低、内存占用小。
- 【关键点 3】协程提供并发不提供并行,线程可并行执行。
- 【易错点 1】误以为协程能自动提升CPU密集型性能。
- 【易错点 2】忽略协程需要配合异步I/O(如epoll)才能发挥优势。
- 【易错点 3】在C++20中std::coroutine是低级工具,需自己实现调度或使用库。