目录
一、创建线程
CreateThread函数:
下面是示例:
编辑
ThreadProc函数解释:
DWORD的本质是 unsigned long PVOID的本质是 void*
二、线程的终止
1.WaitForSingleObject()函数:
示例如下:
2.ExitThread()函数:
示例如下:
3.TerminateThread()函数:
4.CloseHandle()函数:
5.正常return 0;
三、线程的恢复与挂起
1.挂起线程:
①SuspendedThread()函数:
示例如下:
②CreateThread()的第五个参数设为CEREATE_SUSPENDED
四、线程的优先级
一、创建线程
CreateThread函数:
该函数用于创建一个新的线程并在其上运行指定的函数,其返回值是HANDLE类型(句柄),原型如下:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
第一个参数:
指向SECURITY_ATTRIBUTES形态的结构的指针,表示线程内核对象的安全属性
windows 98忽略该参数,windows NT中设为NULL表示使用默认安全属性第二个参数:
初始该线程的堆栈大小,以字节为单位,默认为0即使用默认大小(1MB),在任何情况下,OS会动态延长堆栈大小第三个参数:
是一个指向线程函数的指针。线程将从此函数的入口点开始执行。
函数名称无限制,但必须是以下形式声明:
DWORD WINAPI ThreadProc(PVOID pParam);第四个参数:
传递给线程函数(第三个函数的参数)的参数,是一个指针类型。第五个参数:
线程的创建标志,通常设置为0即可,可选参数如下:
0(或CREATE_SUSPENDED):默认值,创建线程后立即执行线程函数。CREATE_SUSPENDED:创建线程后暂停线程的执行,需要通过ResumeThread激活线程。
CREATE_DETACHED:创建一个分离的线程,不需要手动释放线程资源。
STACK_SIZE_PARAM_IS_A_RESERVATION:将dwStackSize参数解释为堆栈的保留大小。
CREATE_NEW_CONSOLE:创建一个新的控制台窗口,使线程在独立的控制台环境中运行。
CREATE_UNICODE_ENVIRONMENT:使用Unicode字符集解析环境字符串。
第六个参数:
一个指向DWORD类型的指针,用于接收新线程的标识符(ID)
将返回线程的ID号,传入NULL表示不需要返回该线程ID号
下面是示例:
#include <iostream>
#include <windows.h> //调用windows API的头文件
using namespace std;
//线程函数,格式固定
DWORD WINAPI ThreadProc(PVOID lp){
//线程的主体
//…………
return 0;
};
int main()
{
CreateThread(NULL, 0, ThreadProc, NULL, 0, 0);
return 0;
}
ThreadProc函数解释:
该函数即线程入口,ThreadProc 和 lp一样, 名字随意, ThreadProc函数本身作为CreateThread函数的第三个参数,该函数参数由CreateThread函数的第四个参数传入
DWORD的本质是 unsigned long
PVOID的本质是 void*
注意:在多线程环境中,全局变量是所有线程共享的,这意味着多个线程可以同时访问和修改这些全局变量。因为线程是并发的,所以当一个线程在执行过程中修改了全局变量的值时,其他线程在访问同一全局变量时可能会读取到这个修改后的值。
如果一个线程使用new
在堆上分配了内存,并在线程执行过程中释放了这块内存,那么其他线程在访问这块内存时可能会遇到悬挂指针(dangling pointer)或无效内存访问的问题
因此,在多线程编程中,对于共享的资源,包括全局变量和动态分配的内存(如new
操作),必须非常小心。应该通过合适的同步机制(例如互斥锁、条件变量等)来确保多个线程对这些资源的安全访问。这样可以避免潜在的竞态条件和访问无效内存的问题。
在处理动态内存时,最好的做法是由创建它的线程负责释放内存,而不是在其他线程中释放。此外,可以使用智能指针(例如std::shared_ptr
或std::unique_ptr
)来管理动态内存,这样可以避免手动释放内存的问题。
二、线程的终止
1.WaitForSingleObject()函数:
函数原型如下:
DWORD WaitForSingleObject(
HANDLE hHandle, // 要等待的内核对象的句柄,这里是线程句柄
DWORD dwMilliseconds // 等待的时间,以毫秒为单位,INFINITE 表示无限等待
);
第二个参数 dwMilliseconds
表示等待的时间,以毫秒为单位。这个参数控制函数在等待对象状态变化时的行为:
- 如果
dwMilliseconds
的值为INFINITE
(-1),则WaitForSingleObject
会一直阻塞,直到内核对象的状态发生变化。 - 如果
dwMilliseconds
的值为 0,则函数立即检查内核对象的状态,然后返回,不会等待。
其他正整数值表示等待的毫秒数。如果在指定的时间内对象的状态没有发生变化,函数会返回一个值,表示等待超时。
示例如下:
#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI myProThread(PVOID lp)
{
//ExitThread(0); 强制终止线程
Sleep(5000); //Windows 下 Sleep 以毫秒为单位,这里是休眠 5 秒
return 0;
}
int main()
{
DWORD id = 0;
HANDLE handle = CreateThread(NULL, 0, myProThread, NULL, 0, &id);
DWORD result = WaitForSingleObject(handle, 1); //这里是设定等待线程1毫秒,为了测定超时
if (result == WAIT_OBJECT_0)
{
// 线程结束,可以继续处理
cout << "线程结束" << endl;
}
else if (result == WAIT_TIMEOUT)
{
// 超时,可以采取相应的措施
cout << "超时了" << endl;
}
else if (result == WAIT_FAILED)
{
// 函数调用失败,可以通过 GetLastError() 获取错误信息
DWORD dwError = GetLastError();
cout << "线程错误代码为:" << dwError << endl;
}
cout << "该线程的ID是:" << id << endl;
CloseHandle(handle); //关闭线程句柄
return 0;
}
// 等待线程结束
2.ExitThread()函数:
ExitThread
函数可以用于在线程函数内部直接退出线程。但是需要注意,使用此函数会终止线程的执行,不会调用线程的析构函数,也不会释放线程所占用的资源。这可能会导致资源泄漏或程序的不稳定性。
只能在线程内使用,终止该线程
示例如下:
代码:
#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI myProThread(PVOID lp)
{
ExitThread(0); //强制终止线程
Sleep(5000); //Windows 下 Sleep 以毫秒为单位,这里是休眠 5 秒
return 0;
}
int main()
{
DWORD id = 0;
HANDLE handle = CreateThread(NULL, 0, myProThread, NULL, 0, &id);
DWORD result = WaitForSingleObject(handle, 1); //这里是设定等待线程1毫秒,为了测定超时
if (result == WAIT_OBJECT_0)
{
// 线程结束,可以继续处理
cout << "线程结束" << endl;
}
else if (result == WAIT_TIMEOUT)
{
// 超时,可以采取相应的措施
cout << "超时了" << endl;
}
else if (result == WAIT_FAILED)
{
// 函数调用失败,可以通过 GetLastError() 获取错误信息
DWORD dwError = GetLastError();
cout << "线程错误代码为:" << dwError << endl;
}
cout << "该线程的ID是:" << id << endl;
CloseHandle(handle); //关闭线程句柄
return 0;
}
// 等待线程结束
运行结果:
3.TerminateThread()函数:
TerminateThread
函数可以用来强制终止一个线程。然而,这个函数不安全,因为它会立即终止线程的执行,而不管线程正在做什么。这可能会导致未释放的资源,不稳定的状态,以及可能影响整个进程的问题。推荐避免使用这个函数。
可以在线程外使用,终止指定线程
这里不做示例,只提供函数如何使用:
TerminateThread(hThread, 0); //第一个参数是线程句柄,第二个参数是退出码(无意义)
4.CloseHandle()函数:
如果你有线程的句柄,可以使用 CloseHandle
函数来关闭线程句柄。这不会终止线程,但会释放句柄所占用的资源。这个函数的主要作用是清理句柄,而不是终止线程。
注:在关闭线程句柄之前,通常应该确保线程已经退出或者至少没有使用线程句柄引用了线程。
5.正常return 0;
不做赘述。
三、线程的恢复与挂起
1.挂起线程:
PS:下列代码输出求挂起数时,加了1。
①SuspendedThread()函数:
参数为线程句柄,返回值为先前的挂起数(即调用该函数次数)
示例如下:
代码:
#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI myProThread(PVOID lp)
{
//ExitThread(0); //强制终止线程
Sleep(5000); //Windows 下 Sleep 以毫秒为单位,这里是休眠 5 秒
return 0;
}
int main()
{
DWORD id = 0;
HANDLE handle = CreateThread(NULL, 0, myProThread, NULL, 0, &id); //第五个参数表示创建完成时挂起线程
cout << "该线程的ID是:" << id << endl;
DWORD result = WaitForSingleObject(handle, 3000); //这里是设定等待线程1毫秒,为了测定超时
// 挂起线程
DWORD suspendCount = SuspendThread(handle);
cout << "初始挂起数为:" << suspendCount + 1 << endl;
// 检查是否成功挂起线程
if (suspendCount != -1) {
cout << "线程已挂起" << endl;
} else {
cerr << "无法挂起线程" << endl;
}
if (result == WAIT_OBJECT_0)
{
// 线程结束,可以继续处理
cout << "线程结束" << endl;
}
else if (result == WAIT_TIMEOUT)
{
// 超时,可以采取相应的措施
cout << "超时了" << endl;
}
else if (result == WAIT_FAILED)
{
// 函数调用失败,可以通过 GetLastError() 获取错误信息
DWORD dwError = GetLastError();
cout << "线程错误代码为:" << dwError << endl;
}
CloseHandle(handle); //关闭线程句柄
return 0;
}
结果:
②CreateThread()的第五个参数设为CEREATE_SUSPENDED
代码同上,只是在创建线程时,把第五个参数设为CEREATE_SUSPENDED了
结果:
四、线程的优先级
在 Windows 下,C++ 程序可以使用线程库(Thread Library)来创建和管理线程。在 Windows 中,线程优先级用于确定操作系统在有多个线程要执行时,如何进行线程调度。Windows 提供了一组函数和常量来设置和获取线程的优先级。以下是关于 Windows 下 C++ 线程优先级的重要信息:
1. **线程优先级范围:** 在 Windows 系统中,线程的优先级范围通常是从 0(最低优先级)到 31(最高优先级)。
2. **默认优先级:** 当创建一个新线程时,它默认会继承创建它的线程的优先级。
3. **设置线程优先级:** 可以使用 `SetThreadPriority` 函数来设置线程的优先级。该函数的原型如下:
BOOL SetThreadPriority(
HANDLE hThread,
int nPriority
);- `hThread`:要设置优先级的线程句柄。
- `nPriority`:要设置的优先级,可以是以下常量之一:
- `THREAD_PRIORITY_IDLE`
- `THREAD_PRIORITY_LOWEST`
- `THREAD_PRIORITY_BELOW_NORMAL`
- `THREAD_PRIORITY_NORMAL`
- `THREAD_PRIORITY_ABOVE_NORMAL`
- `THREAD_PRIORITY_HIGHEST`
- `THREAD_PRIORITY_TIME_CRITICAL`
4. **获取线程优先级:** 可以使用 `GetThreadPriority` 函数来获取线程的当前优先级。该函数的原型如下:
int GetThreadPriority(
HANDLE hThread
);
- `hThread`:要查询优先级的线程句柄。
需要注意的是,虽然可以通过设置线程的优先级来影响线程的调度,但是过度使用优先级可能会导致问题,如饥饿、不公平调度等。正确地使用同步机制和合适的线程优先级,以确保程序的稳定性和可预测性,是良好的多线程编程实践的一部分。
在实际开发中,除非你有明确的需求,一般不建议频繁地改变线程的优先级,而是让操作系统自行管理线程调度,以确保整个系统的平稳运行。