下个迭代有个任务很有趣,用大量的线程去访问一个接口,直至其崩溃为止,这就需要多线程的知识,这也不是什么难事,总结一下C++中的多线程方法:std、boost、pthread、windows api。
目录
一、多线程预备知识
二、C++11 std::thread
2.1 创建线程
2.2 线程管理
2.3 传递参数
2.4 线程间通信
2.4.1 使用共享变量和互斥量(互斥锁):
2.4.2 使用共享内存
2.5 其他线程相关信息
三、boost多线程库
四、POSIX线程(Linux&Unix)
五、 Windows线程库
5.1 进程使用
5.2 进程通信
一、多线程预备知识
多线程本质是将程序的运行拆分为部分,每个线程都有自己的执行上下文,包括它的程序计数器,寄存器和栈,并且它们独立地执行,互不干扰。我们必须要明确的是多线程的实现依赖硬件条件:多线程需要多核处理器。多核处理器可以同时执行多个线程,因此多线程可以利用多核处理器的多个内核来实现更快的处理速度和更高的性能。通过使用多核处理器,可以把多个线程并行执行,从而实现更高的性能和更快的处理速度。
比如说,在图像处理应用中,如果使用多核处理器,可以把图像处理任务分成多个独立的部分,并使用多个线程分别执行每个部分。每例如,线程 1 可以处理图像的颜色校正,线程 2 可以处理图像的缩放,线程 3 可以处理图像的旋转等。由于每个线程都可以同时执行,因此处理速度比单线程处理要快得多。需要注意的是,在这种情况下,还需要考虑线程间的同步和通信,以确保线程之间的数据一致性。
二、C++11 std::thread
2.1 创建线程
#include <iostream>
#include <thread>
void foo()
{
std::cout << "Hello from thread" << std::endl;
}
int main()
{
std::thread t1(foo);
t1.join();
return 0;
}
在上面的代码中,通过 std::thread t1(foo)
创建了一个新线程,并在新线程中执行了函数 foo
。
2.2 线程管理
#include <iostream>
#include <thread>
void foo()
{
std::cout << "Hello from thread" << std::endl;
}
int main()
{
std::thread t1(foo);
t1.join();
t1.detach();
return 0;
}
在上面的代码中,通过调用 t1.join()
可以等待线程 t1
结束,并通过调用 t1.detach()
可以分离线程 t1
,使线程在后台运行。
std::thread::detach()
方法是分离线程的方法,通过调用 detach()
方法,可以让线程在后台独立运行,不需要等待线程结束即可返回。与调用 join()
方法不同,分离的线程不受父线程的控制,因此如果父线程结束,分离的线程不会因此结束。请注意,分离的线程一旦开始执行,它将独立运行,不受任何控制,因此不能在父线程中等待它的结束或者通过其他方式获取其结果。如果需要等待线程结束,请使用 join()
方法。
分离线程的主要作用是让线程在后台独立运行,不需要等待线程结束即可返回。这样可以在不需要等待线程结束的情况下,在父线程中执行其他任务,从而提高程序的效率。
2.3 传递参数
#include <iostream>
#include <thread>
void foo(int x)
{
std::cout << "Hello from thread, x = " << x << std::endl;
}
int main()
{
std::thread t1(foo, 10);
t1.join();
return 0;
}
在上面的代码中,通过在创建线程时传递参数 10
,并在函数 foo
中读取参数,实现了在线程中传递参数的功能。
2.4 线程间通信
2.4.1 使用共享变量和互斥量(互斥锁):
#include <iostream>
#include <thread>
#include <mutex>
int g_num = 0;
std::mutex g_num_mutex;
void print_num() {
while (true) {
g_num_mutex.lock();
if (g_num >= 100) {
g_num_mutex.unlock();
break;
}
std::cout << "Thread " << std::this_thread::get_id() << ": " << g_num++ << std::endl;
g_num_mutex.unlock();
}
}
int main() {
std::thread t1(print_num);
std::thread t2(print_num);
t1.join();
t2.join();
return 0;
}
我们定义了一个全局变量g_num,两个线程分别运行print_num函数。print_num函数不断地对g_num变量进行读写操作,并使用互斥量g_num_mutex对该变量进行加锁。这样可以确保在每次读写操作时,不会有其他线程访问g_num变量。如果g_num的值已经大于等于100,则表示线程退出。最后主线程调用join函数,等待其他线程退出。
2.4.2 使用共享内存
#include <iostream>
#include <thread>
#include <string>
using namespace std;
string message;
void write_message()
{
message = "Hello World";
}
void read_message()
{
cout << message << endl;
}
int main()
{
thread t1(write_message);
thread t2(read_message);
t1.join();
t2.join();
return 0;
}
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
类创建多线程的简单示例:
#include <iostream>
#include <boost/thread.hpp>
void worker_thread()
{
std::cout << "Worker thread is running" << std::endl;
}
int main()
{
boost::thread worker(worker_thread);
std::cout << "Main thread is running" << std::endl;
worker.join();
return 0;
}
四、POSIX线程(Linux&Unix)
C++ POSIX 线程(pthread)库可以帮助您在 C++ 中创建和管理多线程。下面是一个创建和启动一个新线程的示例代码:
#include <pthread.h>
#include <iostream>
void *thread_func(void *arg)
{
std::cout << "Thread started with argument: " << *((int *)arg) << std::endl;
return NULL;
}
int main()
{
pthread_t thread_id;
int arg = 42;
// 创建新线程
int result = pthread_create(&thread_id, NULL, thread_func, &arg);
if (result != 0) {
std::cerr << "Error: pthread_create() failed" << std::endl;
return 1;
}
// 等待线程结束
result = pthread_join(thread_id, NULL);
if (result != 0) {
std::cerr << "Error: pthread_join() failed" << std::endl;
return 1;
}
return 0;
}
这段代码中,pthread_create
函数用于创建一个新线程,并启动它。第一个参数是一个指向线程 ID 的指针,第二个参数指定了线程的属性,通常为 NULL
,第三个参数是线程函数的地址,最后一个参数是传递给线程函数的参数。
pthread_join
函数等待线程结束,第一个参数是线程 ID,第二个参数是接收线程返回值的指针,通常为 NULL
。
五、 Windows线程库
5.1 进程使用
下面是使用Windows API创建线程并打印数字并获取重要的线程信息的代码示例:
#include <iostream>
#include <Windows.h>
DWORD WINAPI ThreadFunction(LPVOID lpParam)
{
// 获取当前线程ID
DWORD threadId = GetCurrentThreadId();
std::cout << "Thread ID: " << threadId << std::endl;
// 获取线程传入的参数
int threadParam = *(int*)lpParam;
for (int i = 0; i < threadParam; i++)
{
std::cout << i << std::endl;
}
return 0;
}
int main()
{
int num = 10;
// 创建线程
HANDLE hThread = CreateThread(NULL, 0, ThreadFunction, &num, 0, NULL);
// 等待线程结束
WaitForSingleObject(hThread, INFINITE);
// 关闭线程句柄
CloseHandle(hThread);
return 0;
}
5.2 进程通信
在 Windows API 中,可以使用 CreateThread
函数创建线程,以下是通过共享变量实现线程间通信的代码示例:
#include <Windows.h>
#include <iostream>
DWORD WINAPI ThreadFunction1(LPVOID lpParam)
{
int* sharedVariable = (int*)lpParam;
while (true)
{
if (*sharedVariable == 0)
{
std::cout << "Thread 1: sharedVariable = " << *sharedVariable << std::endl;
*sharedVariable = 1;
}
}
return 0;
}
DWORD WINAPI ThreadFunction2(LPVOID lpParam)
{
int* sharedVariable = (int*)lpParam;
while (true)
{
if (*sharedVariable == 1)
{
std::cout << "Thread 2: sharedVariable = " << *sharedVariable << std::endl;
*sharedVariable = 0;
}
}
return 0;
}
int main()
{
int sharedVariable = 0;
HANDLE hThread1 = CreateThread(NULL, 0, ThreadFunction1, &sharedVariable, 0, NULL);
HANDLE hThread2 = CreateThread(NULL, 0, ThreadFunction2, &sharedVariable, 0, NULL);
WaitForMultipleObjects(2, &hThread1, TRUE, INFINITE);
CloseHandle(hThread1);
CloseHandle(hThread2);
return 0;
}
在主函数中,我们调用了 CreateThread
函数创建了两个线程,将共享变量 sharedVariable
的地址作为参数传入。线程 1 和线程 2 都通过判断共享变量的值来实现通信,它们每当共享变量的值变化时都会输出当前的值。
在这个例子中,WaitForMultipleObjects函数将等待两个线程hThread1和hThread2完成,并且要求等待所有事件对象完成,因此参数bWaitAll的值为TRUE。并且dwMilliseconds的值为INFINITE,所以等待时间是无限的。