上一篇笼统介绍了如何创建线程以及线程如何和类成员函数通信,本篇将主要介绍:
线程的相关操作
1.线程的挂起和恢复:SuspendThread、ResumeThread
在线程创建并运行后,用户可以对线程执行挂起和恢复操作
挂起就是指暂停线程的执行,有暂停就有恢复,之后用户可以通过指定的操作来恢复线程的正常执行。
注意:线程的挂起和恢复是有次数限制的,可以进行多次挂起,但之后想要正常执行就需要多次恢复操作。
//线程函数
UINT __cdecl CThreadDlg::MyControllingFunction(LPVOID pParam)
{
int tipMes = (int)pParam;
CString strMsg;
while (TRUE)
{
strMsg.Format(_T("%d"), tipMes++);
OutputDebugString(strMsg);
Sleep(100);
}
return 100;
}
//定义一个线程句柄
HANDLE hThread = NULL;
//开启一个线程
void CThreadDlg::OnBnClickedCreatethraedButton()
{
// TODO: 在此添加控件通知处理程序代码
CWinThread* pThread = AfxBeginThread(MyControllingFunction, (LPVOID)123);
hThread = pThread->m_hThread;//获得线程的句柄
}
void CThreadDlg::OnBnClickedSuspendthreadButton2()
{
// TODO: 在此添加控件通知处理程序代码
SuspendThread(hThread);
}
void CThreadDlg::OnBnClickedResumethreadButton3()
{
// TODO: 在此添加控件通知处理程序代码
ResumeThread(hThread);
}
2.线程的相对优先级
线程的相对优先级总共有七个优先级,分别为:
- THREAD_PRIORITY_TIME_CRITICAL 关键时间(最高)
- THREAD_PRIORITY_HIGHEST 最高,其实是次高
- THREAD_PRIORITY_ABOVE_NORMAL 高于标准
- THREAD_PRIORITY_NORMAL 标准
- THREAD_PRIORITY_BELOW_NORMAL 低于标准
- THREAD_PRIORITY_LOWEST 最低,其实是次底
- THREAD_PRIORITY_IDLE 空闲,最低
在AfxBeginThread创建的线程时候指定,而CreateThread可以在创建后在指定,可以通过下面两个函数进行设置:
获取:GetThreadPriority
设置:SetThreadPriority
设置线程优先级的目的:
1.cpu会以更多的的时间执行高优先级的线程
2.高优先级的线程可以打断低优先级的线程
3.线程的退出与终结
1.最好的方式:让线程函数主动退出,或者return;
可以保证线程函数里面的对象的析构函数被调用,以及线程申请的相关空间被释放;
2.线程自己主动退出,win32API可以调用ExitThread(MFC中使用AfxEndThread);
线程函数里面的对象的析构函数不会被调用,线程申请的相关空间被释放,所以,在c语言里可以使用该函数退出线程,但是c++不建议这样使用,因为c++有类,需要调用析构函数才能释放类的资源
3.其他程序的强行结束线程:
可以调用TerminateThread,此函数非常危险,
被结束的线程不会得到任何通知,线程申请的相关资源也不会被释放,所以谨慎使用。
4.线程退出码的获取:
GetExitCodeThread,前提是句柄有效,不被关闭。
在使用退出码时
HANDLE hThread = NULL;
DWORD dwExitCode = 0;
//开启线程,同时关闭自动释放线程,同时把句柄导出以此获取线程函数结束返回码
CWinThread* pThread = AfxBeginThread(StartCameraTest, NULL, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
pThread->m_bAutoDelete = FALSE;//关闭自动释放资源的设置
hThread = pThread->m_hThread;//获取句柄
//获取线程函数的返回码
GetExitCodeThread(hThread, &dwExitCode);
//如果线程结束,返回码dwExitCode为100该值在线程函数最后return即可,值可以自己设定,但是有个限制,具体请查看文档,此时在释放线程句柄就安全了,同时把返回码初始成0
CloseHandle(hThread);
dwExitCode = 0;
线程间的通信
1.全局变量方式:
定义一个全局变量,开启两个线程,一个对变量进行自增操作,一个进行读取
//全局变量
int g_Num = 100;
//写数据线程
UINT __cdecl CThreadDlg::ThreadWriteProc(LPVOID pParam)
{
while (TRUE)
{
++g_Num;
Sleep(50);
}
return 100;
}
//读数据线程
UINT __cdecl CThreadDlg::ThreadReadProc(LPVOID pParam)
{
CString strMsg;
while (TRUE)
{
strMsg.Format(_T("%d"), g_Num);
OutputDebugString(strMsg);
Sleep(50);
}
}
//定义一个线程句柄
HANDLE hThread = NULL;
//开启一个线程
void CThreadDlg::OnBnClickedCreatethraedButton()
{
// TODO: 在此添加控件通知处理程序代码
CWinThread* pThread = AfxBeginThread(ThreadWriteProc, NULL);
hThread = pThread->m_hThread;//获得线程的句柄
AfxBeginThread(ThreadReadProc, NULL);
}
这个是在同一个.cpp文件夹开启的两个线程和定义的两个全局变量,如果开启的线程在不同的.cpp文件下,如何申请全局变量呢?其实简单加入在1.cpp定义全局变量int a=100;在2.cpp中如何引用这个全局变量的a呢?只需在1.cpp的头文件1.h里加入extern int a;然后其它文件包含该头文件即可。
1.h
1.cpp
#include "1.h"
Int a = 100;
2.h
Extern int a;
2.cpp
#include "2.h"
直接可以使用全局变量a
2.多个线程都能看到的东西即类的成员
在类中声明如下:
public:
CThreadDlg(CWnd* pParent = nullptr); // 标准构造函数
static UINT __cdecl MyControllingFunction(LPVOID pParam);
static UINT __cdecl ThreadWriteProc(LPVOID pParam);
static UINT __cdecl ThreadReadProc(LPVOID pParam);
int m_Num;
//写数据线程
int g_Num = 100;
UINT __cdecl CThreadDlg::ThreadWriteProc(LPVOID pParam)
{
CThreadDlg* pThis = (CThreadDlg*)pParam;
while (TRUE)
{
++(pThis->m_Num);
Sleep(50);
}
return 100;
}
//读数据线程
UINT __cdecl CThreadDlg::ThreadReadProc(LPVOID pParam)
{
CThreadDlg* pThis = (CThreadDlg*)pParam;
CString strMsg;
while (TRUE)
{
strMsg.Format(_T("%d"), (pThis->m_Num));
OutputDebugString(strMsg);
Sleep(50);
}
}
//定义一个线程句柄
HANDLE hThread = NULL;
//开启一个线程
void CThreadDlg::OnBnClickedCreatethraedButton()
{
// 初始化类中的变量m_Num
m_Num = 0;
//开启的线程是无法直接处理类中的变量的,需要传入类的地址。同时我们知道
//该类的就是主对话框,因此直接把主对话框的地址;传给他即可,如:
CWinThread* pThread = AfxBeginThread(ThreadWriteProc, this);
hThread = pThread->m_hThread;//获得线程的句柄
AfxBeginThread(ThreadReadProc, this);
}
3.发消息的方式:PostThreadMessage
该方式需要自定义消息,和绑定,然后一个线程进行消息的发送,另一个线程不停的等待消息和筛选消息类型即只接收我们自定义的消息,一旦接收到立马执行,整体流程是这样的,因为还没深入学习MFC编程,所以这里先放放,以后用到了在系统的总结,但是总体思路是这样的
4.与界面线程相联系
1.创建界面线程的返回值CWinThread类型指针,就是新线程的指针,也因此可以通过强制类型转换对先创建的界面线程进行赋值和信息传递,这里大家需要调试一下。因为界面线程的是没有参数传递,那怎么才能把值传递给新的线程呢?就是通过强制类型的转换:
class CUIThreadApp :
public CWinThread
{
public:
int num;
};
CWinThread* pThread = AfxBeginThread(RUNTIME_CLASS(CUIThreadApp), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
((CUIThreadApp*)pThread)->num = 456;
在初始程序中就好创建这个类的对象,然后类成员的变量num就为456.
这里是通过主线程向子线程进行传递信息,如何在子线程中获取主线程的信息呢
2.在新界面线程中调用AfxGetApp();获取到的是主线程的指针,通过强制类型转换就可以获取主线程的信息了。
3、通过PostThreadMessage方式进行通信,界面线程同样适用,重载界面线程类的PreTranslateMessage即可
以上就是线程间的通信