﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-saiksy-随笔分类-Win32多线程</title><link>http://www.cppblog.com/saiksy/category/16910.html</link><description>记录生活中的点点滴滴</description><language>zh-cn</language><lastBuildDate>Wed, 18 May 2011 14:48:52 GMT</lastBuildDate><pubDate>Wed, 18 May 2011 14:48:52 GMT</pubDate><ttl>60</ttl><item><title> &lt;转&gt;Windows 线程漫谈——界面线程和工作者线程 </title><link>http://www.cppblog.com/saiksy/archive/2011/05/18/146706.html</link><dc:creator>saiksy</dc:creator><author>saiksy</author><pubDate>Wed, 18 May 2011 14:43:00 GMT</pubDate><guid>http://www.cppblog.com/saiksy/archive/2011/05/18/146706.html</guid><wfw:comment>http://www.cppblog.com/saiksy/comments/146706.html</wfw:comment><comments>http://www.cppblog.com/saiksy/archive/2011/05/18/146706.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saiksy/comments/commentRss/146706.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saiksy/services/trackbacks/146706.html</trackback:ping><description><![CDATA[<p>每个系统都有线程，而线程的最重要的作用就是并行处理，提高软件的并发率。针对界面来说，还能提高界面的响应力。</p>
<p>&nbsp;线程分为界面线程和工作者线程，界面实际就是一个线程画出来的东西，这个线程维护一个&#8220;消息队列&#8221;，&#8220;消息队列&#8221;也是界面线程和工作者线程的最大区别，这个词应该进到你的脑子里，根深蒂固的！</p>
<p>如果在界面线程的某个地方停住，这说明它处理不了窗口消息了，所以有时候我们就会看到整个界面无响应了。这种问题后面会提供一个叫　WaitForObjectEx　的函数来解决，我们后面再谈。</p>
<p>线程首先就是它的创建，创建是用下面这个函数：CreateThread;　具体的参数我不说了，自己查ＭＳＤＮ。其中的　Thread1　是线程函数。线程函数是一个全局函数，如下：</p>
<p>DWORD WINAPI Thread1(LPVOID lpParam)<br />{<br />&nbsp; while(1)<br />&nbsp;{<br />&nbsp; OutputDebugString("11111");</p>
<p>&nbsp; Sleep(10);<br />&nbsp;}<br />&nbsp;return 0;<br />}</p>
<p>// 下面这一句是创建线程<br />CreateThread(NULL, 0, Thread1, 0, 0, NULL);</p>
<p>当然我们不能让一个线程自生自灭，那样有可能在你退出程序的时候出现一些莫名其妙的问题，或者丢失一些数据，或者给你弹一个崩溃的对话框等等。。。</p>
<p>所以我们就要对这个线程进行管理，首先就是让它退出。</p>
<p>我们给它的while加上一个 BOOL 变量 g_bExitThread的判断，这样的话，线程函数就变成下面这样：</p>
<p>DWORD WINAPI Thread1(LPVOID lpParam)<br />{<br />&nbsp; while(!g_bExitThread)<br />&nbsp;{<br />&nbsp; OutputDebugString("11111");</p>
<p>&nbsp; Sleep(10);<br />&nbsp;}<br />&nbsp;return 0;<br />}</p>
<p>然后在需要它退出的时候把g_bExitThread设为TRUE，表示，喂，兄弟，你该退出了。</p>
<p>当然我们还要知道它是否成功退出了，因为线程句柄是一个内核对象，所以我们就要用到Windows的WaitForSingleObject来等待了。创建的时候和等待它退出的代码就要改变了，多了一个 HANDLE g_hTrd的变量：</p>
<p>// 创建<br />g_bExitThread = FALSE;<br />g_hTrd = CreateThread(NULL, 0, Thread1, 0, 0, NULL);</p>
<p>// 等待线程结束<br />g_bExitThread = TRUE;</p>
<p>&nbsp;if(g_hTrd != NULL)<br />&nbsp;{<br />&nbsp; DWORD dwRet = WaitForSingleObject(g_hTrd, 5000);<br />&nbsp; if(dwRet == WAIT_OBJECT_0)<br />&nbsp; {<br />&nbsp;&nbsp; AfxMessageBox("Thread exit success!");<br />&nbsp; }<br />&nbsp; else<br />&nbsp; {<br />&nbsp;&nbsp; DWORD dwRet = 0;<br />&nbsp;&nbsp; GetExitCodeThread(g_hTrd, &amp;dwRet);<br />&nbsp;&nbsp; TerminateThread(g_hTrd, dwRet);<br />&nbsp;&nbsp; AfxMessageBox("Thread exit, but not all ok!");<br />&nbsp; }<br />&nbsp; CloseHandle(g_hTrd);<br />&nbsp; g_hTrd = NULL;<br />&nbsp;}</p>
<p>上面说了在界面线程里等待别的线程结束，也就是使用 WaitForSingleObject 的时候会阻塞整个窗口消息的处理，所以我们如果在界面线程里要等待别的内核对象时，我们要采用这种&#8220;等一下，处理一下界面消息&#8221;的方法。我已经写好了一个 WaitForObjectEx 的函数，如下：</p>
<p>// 此函数只能用于界面线程<br />static DWORD WaitForObjectEx( HANDLE hHandle, DWORD dwMilliseconds )<br />{<br />&nbsp;BOOL bRet;<br />&nbsp;MSG msg;<br />&nbsp;INT iWaitRet;<br />&nbsp;int nTimeOut = 0;<br />&nbsp;while( (bRet = ::GetMessage( &amp;msg, NULL, 0, 0 )) != 0)<br />&nbsp;{ <br />&nbsp; if(nTimeOut++ * 20 &gt;= dwMilliseconds)<br />&nbsp;&nbsp; break;</p>
<p>&nbsp; iWaitRet = WaitForSingleObject(hHandle, 20);<br />&nbsp; if(iWaitRet != WAIT_TIMEOUT)<br />&nbsp; {<br />&nbsp;&nbsp; break;<br />&nbsp; }<br />&nbsp; if (bRet == -1)<br />&nbsp; {<br />&nbsp;&nbsp; break;<br />&nbsp; }<br />&nbsp; else<br />&nbsp; {<br />&nbsp;&nbsp; ::TranslateMessage(&amp;msg); <br />&nbsp;&nbsp; ::DispatchMessage(&amp;msg); <br />&nbsp; }<br />&nbsp;}</p>
<p>&nbsp;return iWaitRet;<br />}</p>
<p>很多时候，我们不想把线程作为一个全局函数来使用，所以这个时候我们把线程作为一个类的静态成员对象来写。当然也不能少了刚才的两个变量：退出标志和线程句柄。（设这个类是CTestThreadDlg）</p>
<p>// H 文件 <br />BOOL m_bExitThread;<br />&nbsp;HANDLE m_hTrd;<br />&nbsp;static DWORD WINAPI Thread1(LPVOID lpParam);</p>
<p>// CPP文件，创建的时候把 this 指针传进去，因为类静态成员函数不能访问类的非静态成员，没有this指针<br />//（C++的知识点）<br />&nbsp;m_bExitThread = FALSE;<br />&nbsp;m_hTrd = CreateThread(NULL, 0, Thread1, this, 0, NULL);</p>
<p>线程函数变成了：</p>
<p>&nbsp;DWORD WINAPI CTestThreadDlg::Thread1(LPVOID lpParam)<br />&nbsp;{<br />&nbsp; CTestThreadDlg *pDlg = (CTestThreadDlg*)lpParam;<br />&nbsp; while(!pDlg-&gt;m_bExitThread)<br />&nbsp; {<br />&nbsp;&nbsp; OutputDebugString("11111");<br />&nbsp; <br />&nbsp;&nbsp; Sleep(10);<br />&nbsp; }<br />&nbsp; return 0;<br />&nbsp;}</p>
<p>&nbsp;</p>
<p>当有几个线程一起跑的时候，我们就要注意线程的同步问题了，线程的同步一般来说，是在多个线程共用了资源的时候。比如两个线程都用到了同一个VECTOR，都对VECTOR进行插入操作，不幸的是，VECTOR不是线程安全的，这个时候程序就会崩溃，所以我们就要对VECTOR这个资源做同步，同步的意思是&#8220;我访问的时候，你等待&#8221;。程序大致如下：</p>
<p>DWORD WINAPI CTestThreadDlg::Thread1(LPVOID lpParam)<br />&nbsp;{<br />&nbsp; CTestThreadDlg *pDlg = (CTestThreadDlg*)lpParam;<br />&nbsp; while(!pDlg-&gt;m_bExitThread)<br />&nbsp; {<br />&nbsp;&nbsp; OutputDebugString("11111");<br />&nbsp;<br />&nbsp;&nbsp; pDlg-&gt;m_csForVec.Lock();<br />&nbsp;&nbsp; pDlg-&gt;m_vecTest.push_back("111");<br />&nbsp;&nbsp; pDlg-&gt;m_csForVec.Unlock();<br />&nbsp;<br />&nbsp;&nbsp; Sleep(10);<br />&nbsp; }<br />&nbsp; return 0;<br />&nbsp;}</p>
<p>DWORD WINAPI CTestThreadDlg::Thread2(LPVOID lpParam)<br />{<br />&nbsp;CTestThreadDlg *pDlg = (CTestThreadDlg*)lpParam;<br />&nbsp;while(!pDlg-&gt;m_bExitThread2)<br />&nbsp;{<br />&nbsp; OutputDebugString("222");</p>
<p>&nbsp; pDlg-&gt;m_csForVec.Lock();<br />&nbsp; pDlg-&gt;m_vecTest.push_back("222");<br />&nbsp; pDlg-&gt;m_csForVec.Unlock(); </p>
<p>&nbsp; Sleep(10);<br />&nbsp;}<br />&nbsp;return 0;<br />}</p>
<p>m_csForVec 是一个CCriticalSection变量，这个同步对象和其他的同步变量（事件、信号量、互斥区等）有一些不一样，例如只能在同一个进程的线程间访问、在操作系统的用户态访问，其他的必须进入核心态。所以这样导致了这种关键区的核心对象的速度要比其他的快100倍左右。。。</p>
<p>上面已经说了线程的创建、管理（退出线程、等待线程）、同步等，那我们发现了什么共性呢？作为一个程序员，我们要很敏感的发现这些代码上的共性，这是我们设计代码的主要前提。</p>
<p>首先我们发现上面的线程都有两个变量： <br />BOOL m_bExitThread;&nbsp; // 让线程退出的标志<br />&nbsp;HANDLE m_hTrd;&nbsp; // 线程句柄</p>
<p>另外我们WaitForSingleObject 的时候不能无限等待，所以要多一个 DWORD m_dwWaitTimeOut; </p>
<p>由于我想把线程启动和结束封装起来，所以我设计了这几个接口：</p>
<p>&nbsp;BOOL Start(LPVOID lpParam);&nbsp; //&nbsp; 启动线程，线程所需要的参数从这里传进<br />&nbsp;BOOL End(); // 结束线程<br />&nbsp;virtual void Run(); // 重写Run函数</p>
<p>所以整个的线程封装成以下的类：</p>
<p>// MyThread.h</p>
<p>#ifndef MY_THREAD_H<br />#define MY_THREAD_H</p>
<p>class CMyThread<br />{<br />public:<br />&nbsp;CMyThread();<br />&nbsp;virtual ~CMyThread();</p>
<p>&nbsp;BOOL Start(LPVOID lpParam);<br />&nbsp;BOOL End();<br />&nbsp;virtual void Run();</p>
<p>protected:<br />&nbsp;static DWORD WINAPI Thread(LPVOID lpParam);<br />&nbsp;void RunOnceEnd();</p>
<p>&nbsp;DWORD m_dwWaitTimeOut;<br />&nbsp;BOOL m_bExitThread;<br />&nbsp;HANDLE m_hTrd;<br />&nbsp;LPVOID m_lpParam;<br />};</p>
<p>#endif</p>
<p>// MyThread.Cpp</p>
<p>#include "stdafx.h"<br />#include "MyThread.h"<br />/////////////////////////////////////////////////////////////////////////////<br />// CMyThread<br />CMyThread::CMyThread()<br />{<br />&nbsp;m_bExitThread = FALSE;<br />&nbsp;m_hTrd = NULL;<br />&nbsp;m_dwWaitTimeOut = 5000;<br />}</p>
<p>CMyThread::~CMyThread()<br />{</p>
<p>}</p>
<p>BOOL CMyThread::Start(LPVOID lpParam)<br />{<br />&nbsp;m_lpParam = lpParam;<br />&nbsp;m_bExitThread = FALSE;<br />&nbsp;m_hTrd = CreateThread(NULL, 0, Thread, this, 0, NULL);</p>
<p>&nbsp;return TRUE;<br />}</p>
<p>BOOL CMyThread::End()<br />{<br />&nbsp;m_bExitThread = TRUE;</p>
<p>&nbsp;if(m_hTrd != NULL)<br />&nbsp;{<br />&nbsp; DWORD dwRet = WaitForSingleObject(m_hTrd, m_dwWaitTimeOut);<br />&nbsp; if(dwRet == WAIT_OBJECT_0)<br />&nbsp; {<br />&nbsp;&nbsp; AfxMessageBox("Thread exit success!");<br />&nbsp; }<br />&nbsp; else<br />&nbsp; {<br />&nbsp;&nbsp; DWORD dwRet = 0;<br />&nbsp;&nbsp; GetExitCodeThread(m_hTrd, &amp;dwRet);<br />&nbsp;&nbsp; TerminateThread(m_hTrd, dwRet);<br />&nbsp;&nbsp; AfxMessageBox("Thread fucking exit!");<br />&nbsp; }</p>
<p>&nbsp; CloseHandle(m_hTrd);<br />&nbsp; m_hTrd = NULL;<br />&nbsp;}<br />&nbsp;<br />&nbsp;return TRUE;<br />}</p>
<p>DWORD WINAPI CMyThread::Thread(LPVOID lpParam)<br />{<br />&nbsp;CMyThread *pTrd = (CMyThread *)lpParam;<br />&nbsp;<br />&nbsp;while(!pTrd-&gt;m_bExitThread)<br />&nbsp;{<br />&nbsp; pTrd-&gt;Run();<br />&nbsp;}</p>
<p>&nbsp;return 0;<br />}</p>
<p>void CMyThread::RunOnceEnd()<br />{<br />&nbsp;m_bExitThread = TRUE;<br />&nbsp;CloseHandle(m_hTrd);<br />&nbsp;m_hTrd = NULL;<br />}</p>
<p>void CMyThread::Run()<br />{<br />}</p>
<p>我们需要写我们自己的线程的时候就重载一下这个Run函数</p>
<p>// 派生出一个类<br />class CMyThread1 : public CMyThread<br />{<br />public:<br />&nbsp;virtual void Run();<br />};</p>
<p>// 改写Run函数<br />void CMyThread1::Run()<br />{<br />&nbsp;CTestThreadDlg *pDlg = (CTestThreadDlg *)m_lpParam;</p>
<p>&nbsp;OutputDebugString("222");<br />&nbsp;<br />&nbsp;pDlg-&gt;m_csForVec.Lock();<br />&nbsp;pDlg-&gt;m_vecTest.push_back("222");<br />&nbsp;pDlg-&gt;m_csForVec.Unlock(); <br />&nbsp;<br />&nbsp;Sleep(10);</p>
<p>&nbsp;// 如果此线程只想运行一次，加上下面这句<br />&nbsp;RunOnceEnd();<br />}</p>
<p><br />然后我们之前的两个线程的使用就变成了下面的形式：</p>
<p>CMyThread1 g_t1, g_t2, g_t3;<br />void CTestThreadDlg::OnButton3() <br />{<br />&nbsp;g_t1.Start(this);<br />&nbsp;g_t2.Start(this);<br />&nbsp;g_t3.Start(this); <br />}</p>
<p>void CTestThreadDlg::OnButton4() <br />{<br />&nbsp;g_t1.End();<br />&nbsp;g_t2.End();<br />&nbsp;g_t3.End();&nbsp; <br />}</p>
<p>只需要以下几步：<br />1、派生自己的线程类<br />2、重载Run函数<br />3、调用Start启动线程<br />4、调用End结束线程</p>
<p>当然这种封装方式是我自己喜欢的，封装的目的是方便使用，隐藏细节，诸位看官也可以根据自己的喜好，封装线程的使用方法，如果能在此公开一下你的成果，让我和大家都学习一下你的设计手法，那就真是very good and 3q了！</p>
<p>&nbsp;</p>
<p><br />本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/dylgsy/archive/2008/03/13/2176160.aspx">http://blog.csdn.net/dylgsy/archive/2008/03/13/2176160.aspx</a></p><img src ="http://www.cppblog.com/saiksy/aggbug/146706.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saiksy/" target="_blank">saiksy</a> 2011-05-18 22:43 <a href="http://www.cppblog.com/saiksy/archive/2011/05/18/146706.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>