线程,是操作系统能够运行和计算和调度的最小单位。一个进程至少要包含一个线程,线程要包含在进程中, 是进程实际运作单位。一个进程中可以多有个线程,每个线程并发执行。每个线程有独立的栈空间,线程之间共享静态存储区和堆空间。
那么什么时候要用多线程呢? 当我们要使用并发的时候,就可以使用多线程。 比如,我们要想要解码音视频的同时去播放音频和视频;打印机打印PDF并且界面显示当前进度和提示信息等等。
无论是Windows还是Linux,都提供了创建线程的API提供给我们去创建线程。Windows中使用API CreateThread 创建一个线程。 它的具体表现形式如下:
HANDLE
WINAPI
CreateThread(
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ __drv_aliasesMem LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
LPVOID lpThreadParameter
);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
即,一个参数 LPVOID 类型,返回值为 DWORD 类型的函数指针。 这里需要指出的是,函数需要使用 WINAPI 修饰, 也就是要使用 __stdcall 来修饰函数。
下面是一个简单的示例:
#include <stdlib.h>
#include <iostream>
#include <Windows.h>
// 线程中执行的函数
DWORD WINAPI threadProc(LPVOID lpParameters)
{
bool isRunThread = true;
while (isRunThread)
{
static int number = 0;
if (number >= 5)
{
// 仅执行5次打印
isRunThread = false;
break;
}
std::cout << "This is Run in Thread ID:" << ::GetCurrentThreadId() << ", Number is :" << number++ << std::endl;
Sleep(200);
}
return 0;
}
// Windows API 创建线程
HANDLE createWindowsThread(DWORD *threadId)
{
HANDLE handle = ::CreateThread(nullptr, 0, threadProc, nullptr, 0, threadId);
return handle;
}
int main(int argc, char** argv)
{
DWORD threadId = 0;
HANDLE threadHandle = createWindowsThread(&threadId);
if (threadHandle)
{
// 线程创建成功,打印创建的线程ID和主线程ID
std::cout << "Create Thread Success! Thread ID is " << threadId << std::endl;
std::cout << "Main Thread ID is " << ::GetCurrentThreadId() << std::endl;
// 等待线程结束
::WaitForSingleObject(threadHandle, INFINITE);
}
else // 线程创建失败
std::cout << "Create Thread Error!" << std::endl;
system("pause");
return 0;
}
函数执行结果如下:
Create Thread Success! Thread ID is 162840
Main Thread ID is 9652
This is Run in Thread ID:16284, Number is :0
This is Run in Thread ID:16284, Number is :1
This is Run in Thread ID:16284, Number is :2
This is Run in Thread ID:16284, Number is :3
This is Run in Thread ID:16284, Number is :4
当然也有可能是创建的线程中先打印,后者同时交替着打印。
函数 createWindowsThread 中使用了Windows的创建线程API CreateThread ,
线程中执行函数 threadProc ,while 循环中执行5次后线程退出。
不同操作系统的创建线程的函数是不同的,比如windows中使用 CreateThread ,而Linux中使用 pthread_create 创建。对于跨平台的开发,我们可以使用Qt等第三方开源库完成跨平台的线程创建,也可以使用C++11中提供的方法创建多线程。当前,使用的编译器要支持C++11。
可以使用C++11提供的 std::thread 实现线程的创建。
我们改写一下上面的例子,代码如下:
#include <stdlib.h>
#include <iostream>
#include <thread>
#include <Windows.h>
// 线程中执行的函数
void threadProc(int count)
{
bool isRunThread = true;
while (isRunThread)
{
static int number = 0;
if (number >= count)
{
// 仅执行Count次打印
isRunThread = false;
break;
}
std::cout << "This is Run in Thread ID:" << ::GetCurrentThreadId() << ", Number is :" << number++ << std::endl;
Sleep(200);
}
}
// C++11 创建线程
void createWindowsThread(void)
{
std::thread thread(threadProc, 5);
thread.detach();
}
int main(int argc, char** argv)
{
createWindowsThread();
system("pause");
return 0;
}
程序运行结果如下:
This is Run in Thread ID:10568, Number is :0
This is Run in Thread ID:10568, Number is :1
This is Run in Thread ID:10568, Number is :3
This is Run in Thread ID:10568, Number is :2
This is Run in Thread ID:10568, Number is :4
这里线程中执行的函数可以不指定为 __stdcall 修饰。创建 std::thread 对象,构造函数中传入线程中执行的函数指针和参数,如果没有参数可以不指定。就可以完成线程的创建。
如果想等待线程,可使用 join 函数,我们把代码修改如下:
// C++11 创建线程
std::thread createWindowsThread(void)
{
std::thread thread(threadProc, 5);
return thread;
}
int main(int argc, char** argv)
{
std::thread thread = createWindowsThread();
if (thread.joinable())
thread.join();
std::cout << "Created Thread Finished!" << std::endl;
system("pause");
return 0;
}
执行结果如下:
This is Run in Thread ID:10568, Number is :0
This is Run in Thread ID:10568, Number is :1
This is Run in Thread ID:10568, Number is :2
This is Run in Thread ID:10568, Number is :3
This is Run in Thread ID:10568, Number is :4
Created Thread Finished!
这里因为 std::thread 实现了移动构造,所以当函数中的线程对象被消除的时候,线程的相关信息转移给了新创建的线程函数。
前面说了Windows的API获取线程ID的方法,那么如何使用C++11来获取线程ID呢?
具体实现代码如下:
int stdThreadIdToInt(const std::thread::id& id)
{
std::stringstream oss;
oss << id;
std::string string = oss.str();
int threadId = atol(string.c_str());
return threadId;
}