新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 C/C++编程思想 』 → 多线程管理类 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4019 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 多线程管理类 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 多线程管理类



    下载源代码

      由于最近经常搞些跟线程有关的东西,感觉多线程确实麻烦,线程间要处理好同步与通讯,如果用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#的理解还是很肤浅吧。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/2/16 17:31:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/12/1 7:15:32

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    4,187.500ms