一、多线程预备知识
多线程本质是将程序的运行拆分为部分,每个线程都有自己的执行上下文,包括它的程序计数器,寄存器和栈,并且它们独立地执行,互不干扰。我们必须要明确的是多线程的实现依赖硬件条件:多线程需要多核处理器。多核处理器可以同时执行多个线程,因此多线程可以利用多核处理器的多个内核来实现更快的处理速度和更高的性能。通过使用多核处理器,可以把多个线程并行执行,从而实现更高的性能和更快的处理速度。
比如说,在图像处理应用中,如果使用多核处理器,可以把图像处理任务分成多个独立的部分,并使用多个线程分别执行每个部分。每例如,线程 1 可以处理图像的颜色校正,线程 2 可以处理图像的缩放,线程 3 可以处理图像的旋转等。由于每个线程都可以同时执行,因此处理速度比单线程处理要快得多。需要注意的是,在这种情况下,还需要考虑线程间的同步和通信,以确保线程之间的数据一致性。
二、C++11 std::thread
2.1 创建线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
在上面的代码中,通过std::thread t1(foo)创建了一个新线程,并在新线程中执行了函数foo。
2.2 线程管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
在上面的代码中,通过调用t1.join()可以等待线程t1结束,并通过调用t1.detach()可以分离线程t1,使线程在后台运行。
std::thread::detach()方法是分离线程的方法,通过调用detach()方法,可以让线程在后台独立运行,不需要等待线程结束即可返回。与调用join()方法不同,分离的线程不受父线程的控制,因此如果父线程结束,分离的线程不会因此结束。请注意,分离的线程一旦开始执行,它将独立运行,不受任何控制,因此不能在父线程中等待它的结束或者通过其他方式获取其结果。如果需要等待线程结束,请使用join()方法。
分离线程的主要作用是让线程在后台独立运行,不需要等待线程结束即可返回。这样可以在不需要等待线程结束的情况下,在父线程中执行其他任务,从而提高程序的效率。
2.3 传递参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
在上面的代码中,通过在创建线程时传递参数10,并在函数foo中读取参数,实现了在线程中传递参数的功能。
2.4 线程间通信
2.4.1 使用共享变量和互斥量(互斥锁):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
我们定义了一个全局变量g_num,两个线程分别运行print_num函数。print_num函数不断地对g_num变量进行读写操作,并使用互斥量g_num_mutex对该变量进行加锁。这样可以确保在每次读写操作时,不会有其他线程访问g_num变量。如果g_num的值已经大于等于100,则表示线程退出。最后主线程调用join函数,等待其他线程退出。
2.4.2 使用共享内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
2.5 其他线程相关信息
std::thread::hardware_concurrency():返回当前系统支持的最大线程数。std::thread::native_handle():返回线程的原生句柄,在不同的平台中可能不同。std::thread::joinable():判断线程是否可以被 join,如果线程已经结束或者被分离,则返回 false。std::thread::join():等待线程结束,直到线程结束才返回。std::thread::detach():分离线程,使线程可以在后台运行,并且不需要等待其结束。std::thread::get_id():返回线程ID。
三、boost多线程库
官方文档:
Chapter 38. Thread 4.8.0 - 1.73.0
Boost 库中的boost::thread类可以方便地创建和管理多线程。以下是一个使用 Boost 库中的boost::thread类创建多线程的简单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
四、POSIX线程(Linux&Unix)
C++ POSIX 线程(pthread)库可以帮助您在 C++ 中创建和管理多线程。下面是一个创建和启动一个新线程的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
这段代码中,pthread_create函数用于创建一个新线程,并启动它。第一个参数是一个指向线程 ID 的指针,第二个参数指定了线程的属性,通常为NULL,第三个参数是线程函数的地址,最后一个参数是传递给线程函数的参数。
pthread_join函数等待线程结束,第一个参数是线程 ID,第二个参数是接收线程返回值的指针,通常为NULL。
五、 Windows线程库
5.1 线程使用
下面是使用Windows API创建线程并打印数字并获取重要的线程信息的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
5.2 线程通信
在 Windows API 中,可以使用CreateThread函数创建线程,以下是通过共享变量实现线程间通信的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
在主函数中,我们调用了CreateThread函数创建了两个线程,将共享变量sharedVariable的地址作为参数传入。线程 1 和线程 2 都通过判断共享变量的值来实现通信,它们每当共享变量的值变化时都会输出当前的值。
在这个例子中,WaitForMultipleObjects函数将等待两个线程hThread1和hThread2完成,并且要求等待所有事件对象完成,因此参数bWaitAll的值为TRUE。并且dwMilliseconds的值为INFINITE,所以等待时间是无限的。