这里废话不多讲了,因为项目原因,需要开启线程进行处理,在不了解线程的情况下,直接百度一下,然后就使用了,结果可想而知,出现了异常,所以花了一天时间系统学习一下多线程,这里主要是针对win32编程方面的线程介绍,更多偏向于MFC的多线程开发。
创建线程的三种方式:
方式一: CreatThread(记得关闭线程句柄)
方式二:AfxBeginThread(会自动释放,可以设置不自动释放)
方式三:_beginthreadex(记得关闭线程句柄)
其中方式二和方式三呢都会间接的调用方式一,因为方式一是win32的线程调用API,而MFC为了用户更容易使用多线程进行了封装,其实他们都差多的,下面针对三种方式进行梳理:
大家记得下载微软的MSDN文档查询相关函数的说明文档,至于如何添加文档,请参考这篇文章设定,下面很多内容都是官方文档的内容。
static HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE pfnThreadProc,
void* pvParam,
DWORD dwCreationFlags,
DWORD* pdwThreadId
) throw( );
/*
lpsa:新线程的安全特性。
dwStackSize:新线程的堆栈大小。
pfnThreadProc:线程函数。
pvParam:将传递的参数传递给线程过程。
dwCreationFlags:创建标志(0个或CREATE_SUSPENDED)。
pdwThreadId: [out] 中,若成功,接收新创建的线程的线程ID DWORD变量的地址。
*/
使用例程:
//线程函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int tipMes = (int)lpParameter;
CString strMsg;
strMsg.Format(_T("%d"), tipMes);
AfxMessageBox(strMsg);
return 0;
}
void CThreadTestDlg::OnBnClickedCreatthreadButton()
{
//需要给其传参
DWORD dwthreadID = 0;
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)123, 0, &dwthreadID);
//需要调用CloseHandle,关闭线程句柄,避免泄漏
CloseHandle(hThread);
}
该方式是MFC中的创建方式,主要有两种:界面线程和工作线程
工作线程:
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
界面线程:
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
界面线程和工作线程的最大的区别在于使用具有消息循环
其中上面的参数和方式一一样的,只是这里有默认的参数了
UINT __cdecl MyControllingFunction(LPVOID pParam)
{
int tipMes = (int)pParam;
CString strMsg;
while (TRUE)
{
strMsg.Format(_T("%d"), tipMes++);
OutputDebugString(strMsg);
Sleep(100);
}
return 0;
}
//方式二实现线程的创建
CWinThread* pTherad = AfxBeginThread(MyControllingFunction, (LPVOID)100);
第三种大家自己看说明文档吧。
方法一:是通过传入参数,即传入这个主对话框的指针即可,如下操作
//方式二实现线程的创建
//m_Num变量是主对话框的成员变量
public:
m_Num = 123;
//传入当前主对话框的指针,不直接把变量的地址传过去
CWinThread* pTherad = AfxBeginThread(MyControllingFunction, this);
************************线程函数*************************************************
UINT __cdecl MyControllingFunction(LPVOID pParam)
{
//通过强制类型转换得到主对话框的指针变量,进而可以通过访问其成员变量了
CThreadTestDlg* pThisDlg = (CThreadTestDlg*)pParam;
int tipMes = pThisDlg->m_Num;
CString strMsg;
//信息打印
while (TRUE)
{
strMsg.Format(_T("%d"), tipMes++);
OutputDebugString(strMsg);
Sleep(100);
}
return 0;
}
方法二:
把线程函数作为主对话框类的成员函数:
//方法就是把线程函数称为对话框类的静态成员函数如下:
static UINT __cdecl MyControllingFunction(LPVOID pParam);
//然后带上作用域调用即可
//方式二的线程执行函数的例子
UINT __cdecl CThreadTestDlg::MyControllingFunction(LPVOID pParam)
界面线程和工作线程的最大的区别在于使用具有消息循环,我们先看看在工作线程中创建窗口的情况:
不建议在工作线程中进行窗口的操作
//TestDlg dlg;
模态对话框
//dlg.DoModal();
上面创建的模态对话框是可以使用的,但是不建议使用
//在工作线程中创建一个非模态对话框,看看效果
TestDlg* pTestDlg = new TestDlg();
pTestDlg->Create(IDD_DIALOG1, NULL);
pTestDlg->ShowWindow(SW_SHOW);
//会发现,窗口一闪而过,是因为在创建的时,线程也就执行完了,
//资源会被释放,因此窗口会消失,同时在窗口上无法操作,主要原因是无法接受到消息信息,在后面添加消息循环就可以了
MSG msg = { 0 };
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
最后既然都在工作线程中添加而了消息循环,为什么不直接使用界面线程呢?
同时界面线程的消息循环要比我们自己在工作线程中创建的消息循环更完备
界面线程:
界面线程的创建:
1.从CWinThread类派生自己的类:CUIThreadApp;
2.重载InitInstance(必须重载)与ExitInstance(可选重载)函数
3.在InitInstance函数汇总进行界面的创建
4.调用AfxBeginThread函数开启界面线程:
AfxBeginThread(RUNTIME_CLASS(CUIThreadApp));
这里大家参考别人的吧,因为我战时不涉及界面线程,因此没仔细学习这个线程。