多线程 C++
CS106L p18 Part 6.2 Multithreading
多线程
C++中的multithreading(多线程)是指在程序中并发地执行多个线程的能力。线程是程序执行的基本单元,而多线程则允许程序在同一时间内处理多个任务,通常用来提高程序的效率,尤其是在多核处理器上,通过并行处理来减少任务的总执行时间。多线程广泛应用于高性能计算、图形渲染、网络通信、游戏开发等领域。
1. 线程的基本概念
线程是程序中的一个执行流,它有自己的执行栈、程序计数器和局部变量。多个线程共享同一进程的资源,例如内存、文件句柄等。相比于单一的执行流(即单线程),多线程能够让程序在同一时刻执行多个任务,从而更充分地利用多核处理器的计算能力。
线程和进程的区别:
- 进程是资源分配的基本单位,包含了地址空间、数据、堆栈等。
- 线程是程序执行的基本单位,属于进程的一部分,它共享进程的资源。
在C++中,线程的创建、管理和同步是通过标准库提供的工具实现的,尤其是C++11引入的<thread>
库和相关功能。
2. C++中的多线程实现
从C++11开始,C++标准库加入了对多线程的直接支持,提供了<thread>
头文件,其中包括了创建和管理线程的类和函数。以下是一些常见的多线程编程元素:
2.1 线程的创建与执行
要创建一个线程,首先需要定义一个函数或者可调用对象(如函数指针、lambda表达式等),然后将其传递给std::thread
类的构造函数。
1 |
|
在上述代码中:
std::thread t(hello);
:创建并启动一个新线程,执行hello
函数。t.join();
:主线程等待子线程t
执行完成。
2.2 传递参数到线程
如果需要传递参数给线程,可以直接在创建线程时传递。例如:
1 |
|
这里,print_number
函数接收一个参数x
,并在线程中打印出来。
2.3 线程的同步与互斥
多线程编程的核心挑战之一是共享资源的同步问题。当多个线程访问同一资源时,可能会发生数据竞争(data race),导致不一致的结果。为了防止这种情况,可以使用互斥锁(mutex)来同步对共享资源的访问。
C++标准库提供了std::mutex
来实现互斥锁:
1 |
|
在上述代码中,std::mutex mtx
是一个互斥锁,std::lock_guard<std::mutex>
用于自动加锁和解锁。lock_guard
确保在作用域结束时,锁会自动被释放,从而避免死锁的发生。
2.4 线程的并发和并行
- 并发(Concurrency)指的是多个任务在同一时间段内交替执行,可能是在单核或多核处理器上。在多核处理器上,任务可以在不同核心上并行执行,但并不一定同时执行。
- 并行(Parallelism)是指多个任务在同一时刻在不同的处理器核心上执行,通常只有在拥有多核CPU的系统中,才能实现真正的并行。
C++的多线程通常提供并发执行的能力,实际的并行执行取决于硬件和操作系统的调度。
2.5 线程的同步机制:条件变量
在多线程程序中,有时需要让某些线程等待某些条件发生。这种情况下,可以使用条件变量(std::condition_variable
)。
条件变量用于在多线程中同步事件的发生。可以在某个线程上等待某个条件满足,然后再继续执行。例如:
1 |
|
在这个例子中,print_id
函数的线程会等待ready
变量变为true
,只有在go()
函数中通知后,所有线程才会开始执行。
3. 多线程中的常见问题
多线程编程虽然能够带来性能上的优势,但也伴随着许多挑战和潜在问题,包括但不限于:
- 数据竞争(Data Race):多个线程并发地读写共享数据,导致数据不一致。
- 死锁(Deadlock):多个线程因互相等待对方释放资源,导致程序无法继续执行。
- 活锁(Livelock):类似死锁,线程不断尝试某些操作,但始终无法完成任务。
- 优先级反转(Priority Inversion):低优先级的线程占用了资源,导致高优先级线程无法执行。
为了避免这些问题,必须合理设计线程同步和资源管理策略。
4. 总结
C++中的多线程编程能够帮助开发者更好地利用现代多核处理器,提高程序的并发性和执行效率。C++11引入了强大的线程支持,包括线程创建、同步机制(如互斥锁、条件变量)等。掌握多线程的基本操作和同步原理是开发高效、稳定应用程序的关键。然而,多线程编程也需要小心避免数据竞争、死锁等问题,因此需要更加细致地设计和调试。