程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 多線程管理類

多線程管理類

編輯:關於VC++

由於最近經常搞些跟線程有關的東西,感覺多線程確實麻煩,線程間要處理好同步與通訊,如果用CWinThread好一點,直接是一個線程對象,如果用AfxBeginThread,那必須定個全局函數,或者寫個靜態函數,一般是傳個this指針進去,然後再用這個指針調用本類函數的成員函數,用起來比較麻煩,現在問題是能不能不用全局或者靜態函數來實現呢,於是我實現了這個類,來所簡化多線程的創建和關閉的操作。

現在簡要看一下類的數據和成員函數

template<class T>
class CYGMulThread
{
protected:
  typedef void (T::* _pThreadFunc)(void );
  static _pThreadFunc ThreadFunc;
  static BOOL s_IsBreaking; //用以判斷線程是否要中斷
  static CWaitDlg s_Waitdlg;//等待對話框
  //內部線程
  static UINT ThreadProc(LPVOID lpParam);
public:
  //在線程內部使用,用以判斷有沒有要結束的信號
  static BOOL IsBreakThread() ;
  //----------------------------非靜態
protected:
  CYGMulThread();
  virtual ~CYGMulThread();
  CWinThread **m_pThread;//線程數組指針
  int m_nThreadCount;
  //處理windows的消息
  void PeekMessageLoop();
public:
  int GetThreadCount() const ;
  //pFunc=輸入一個void f(void)的成員函數,nThreadCount=線程數目
  void YGBeginFuncThread(_pThreadFunc pFunc,int nThreadCount);
  //結束線程時調用
  bool EndAllThread(const char *szMsg);
};

一、這裡先說一下這個類的使用方法

class CMulThreadDlg : public CDialog,private CYGMulThread
{
// Construction
public:
  CMulThreadDlg(CWnd* pParent = NULL);  // standard constructor
  void ReleaseShow2();
  void ReleaseShow();
  void ThreadFunc();
// Dialog Data
  //{{AFX_DATA(CMulThreadDlg)
  enum { IDD = IDD_MULTHREAD_DIALOG };
  CYGEdit  m_edShow;
  //}}AFX_DATA
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CMulThreadDlg)
  protected:
  virtual void DoDataExchange(CDataExchange* pDX);  // DDX/DDV support
  //}}AFX_VIRTUAL
// Implementation
protected:
  HICON m_hIcon;
  // Generated message map functions
  //{{AFX_MSG(CMulThreadDlg)
  virtual BOOL OnInitDialog();
  afx_msg void OnPaint();
  afx_msg HCURSOR OnQueryDragIcon();
  afx_msg void OnBtnDebug();
  afx_msg void OnBtnStop();
  afx_msg void OnBtnTest1();
  afx_msg void OnBtnTest2();
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};

這裡要注意兩個地方:

1.CYGMulThread的模板是用被派生出來的類,這裡的好處,就是可以使CYGMulThread可以使用被派生出來的東西,至於還有什麼好處,有興趣的朋友可以看一下ATL和WTL裡的代碼,裡面大量使用這種結構。

2.這裡用的私有繼承,當然也可以用公用繼承:),個人覺得這樣私有繼承,數據封裝性好一點。

CMulThreadDlg有了這樣的頭定義後,就可以使用下面的行為了

void CMulThreadDlg::ThreadFunc()
{
  DWORD dwID=GetCurrentThreadId();
  while (1)
  {
//    g_cs1.Lock();
    TRACE("線程:%x\t路過\n",dwID);
//    g_cs1.Unlock();
    Sleep(200);
    if (IsBreakThread())
    {
//      g_cs1.Lock();
      TRACE("線程:%x要結束了!\n",dwID);
//      g_cs1.Unlock();
      return ;
    }
    Sleep(10000);
  }
}
//創建線程
void CMulThreadDlg::OnBtnDebug()
{
  YGBeginFuncThread(ThreadFunc,10);//創建10個線程,其函數為ThreadFunc,這裡ThreadFunc是CMulThreadDlg的成員函數
}
//結束線程
void CMulThreadDlg::OnBtnStop()
{
  EndAllThread("正要結束線程....");
}

二、下面說明一下代碼的內容

其實創建線程的操作很簡單,最令人煩不勝煩的地方是結束線程的代碼。

一般其流程是:

1.通知所以線程要結束

2.等待所以線程結束

通知其實倒挺好通知,問題是等待!

第一個問題,主線程界面,考慮的第一個函數是WaitForMultipleObjects,問題是主線用這個函數等待子線程結束時,令主線程會處於休眠狀態,如果等待時間長的話,程序就會像死了似的。於是把函數換為MsgWaitForMultipleObjects,令其每次只等待一個線程,而且將其參數bWaitAll設為FALSE,並在調用這個函數前,用PeekMessageLoop處理完Windows留下的消息,這樣主界面看起來就不會出問題 。

第二個問題,句柄的復制,因為在MsgWaitForMultipleObjects裡,需要輸入要等待線程的句柄數組,但AfxBeginThread開啟的線程,當線程結束後,其線程句柄就無效,句柄不能普通地賦值,只能用DuplicateHandle將其復制 。

第三個問題,提示,當主線程長時間等待子線程時,是不是應有個提示框之類的東西提示呢,然後主主界面的任何其它操作都不能用,就像我們平時用MessageBox時,我們只能先把這個Box去掉之後,才能操作界面的其它東西,但這裡不用這個方法,這裡只能Create一個非模態對話框,但這個框要達到像模態對話框的效果,我用spy++看了一下才看想到原來可以這樣

//pWnd是主界面的指針
pWnd->EnableWindow(false);
//Create一個非模態對話框
//等待所以線程結束
pWnd->EnableWindow(true);
pWnd->ShowWindow(SW_RESTORE);

下面是主要代碼:

bool EndAllThread(const char *szMsg)
{
  if (!m_pThread) return false;//線程句柄為空
  HANDLE pProcess=GetCurrentProcess();
  BOOL bRet;
  int i(0),nRet;
  HANDLE *pHandle=new HANDLE[m_nThreadCount];
  T *pWnd=static_cast<T *>(this);
  pWnd->EnableWindow(false);

  if (szMsg)
  {
    s_Waitdlg.m_strshow=szMsg;
    s_Waitdlg.SetCancelDisable();//使取消按鈕無效
    s_Waitdlg.Create((char *)IDD_YGDIALOG_WAITING,pWnd);
    s_Waitdlg.CenterWindow(NULL);
    s_Waitdlg.ShowWindow(SW_SHOW);
  }
  for(i=0;i<m_nThreadCount;i++)
  {
    //將句柄復制
    bRet=DuplicateHandle(pProcess,m_pThread[i]->m_hThread,pProcess,&pHandle[i],DUPLICATE_SAME_ACCESS,
      true,DUPLICATE_SAME_ACCESS);
    //保證所復制的句柄都要有效
    if (!bRet)
    {
      Sleep(100);
      TRACE("----------------DuplicateHandle 失敗!\n");
      i--;continue;
    }
  }
  s_IsBreaking=true;//將線程結束
  i=0;
  //等待線程結束
  while (i!=m_nThreadCount)//直到所以線程的m_hThread都已經結束時則i==m_nThreadCount
  {
    for(i=0;i<m_nThreadCount;i++)
    {
      PeekMessageLoop();//先處理多余的Windows消息
      //這裡一定要用MsgWaitForMultipleObjects,不能用WaitForMultipleObjects,因為這個不能處理消息
      nRet=MsgWaitForMultipleObjects(1,pHandle+i,false,INFINITE,QS_ALLEVENTS);
      TRACE("nRet=%d,i=%d\n",nRet,i);
      if (nRet==WAIT_OBJECT_0+1) //只有消息Event,沒有線程的Event
      {
        Sleep(20); //等會兒
        break;
      }
      if (nRet==WAIT_FAILED ) break;//發生錯誤,無須等待了
    }
    if (nRet==WAIT_FAILED ) break;
  }
  delete [] m_pThread;
  delete [] pHandle;
  m_pThread=NULL;
  m_nThreadCount=0;
  pWnd->EnableWindow(true);
  pWnd->ShowWindow(SW_RESTORE);
  if (IsWindow(s_Waitdlg.m_hWnd)) s_Waitdlg.DestroyWindow();
  if (nRet==WAIT_FAILED ) false;
  return true;
}

結束:

本人文筆不好,以上內容表達可能錯漏百出,請各大蝦不要見笑,詳細還是見代碼吧,如程序有什麼問題請用下面的方式聯系。

無數遍想不搞MFC,搞界面用MFC確實麻煩,但又捨不得C++。看了幾下搞界面好的Delphi,但它PASCAL語法實在讓人難以接受,語法功能太弱,就連變量聲明的位置都受到限制,操作符重載居然不支持,模板就更不用說了。用.net,把C++擴展了,搞得不知道像個什麼樣子,還不如用C#,但C#裡沒有了指針,內存申請了,還不能手動釋放,可能我對C#的理解還是很膚淺吧。還是覺得C++是最好計算機語言,但可惡的M$好像要放棄MFC,MFC一直沒有什麼大的改進,有跟我志同道合的朋友歡迎跟我聯系。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved