65.9K
CodeProject 正在变化。 阅读更多。
Home

在 MFC 和 Win32 中初始化线程

2021年10月7日

CPOL

5分钟阅读

viewsIcon

23439

downloadIcon

1952

在 MFC 和 Win32 中启动线程以及一些处理示例

MFC

Win32

 

InitThreadsTitle

引言

在我的一些项目开发中,多线程技术变得很有用。一如既往,我查看了 CodePoject 并找到了 Arman S. 的精彩文章 《MFC 多线程应用程序中的同步》。文章的思路和脚本代码都很好。这是熟悉多线程的良好起点。

出于习惯,演示实现的效果并不好。在绘制新椭圆之前,用白色矩形绘制旧椭圆位置是一个糟糕的想法。当椭圆重叠时,会出现可见的白色间隙。

因此,我冒昧地通过标准的 MSVS AppWizard 程序改进了性能,并添加了一些处理功能。

背景

上述文章写于 2007 年,因此提供的 WorkerThreads 项目与最新版本的 MSVS 不直接兼容。因此,我创建了一个新的、单文档项目,名为 WorkerThreads,使用 MSVS-2012 pro,将 WorkerThreadsView.hWorkerThreadsView.cpp 的代码替换为 Arman S. 项目中的相同代码,并从相同的源文件中包含了 Thread.hThread.cpp。编译产生的可执行文件产生了如下视频演示:

workerthreads

为了改进上述性能,我开发了一个新项目 InitThreads。可执行文件 InitThreads.exe 是使用 MSVS-2012 pro 构建的。

演示说明

在开始构建提供的项目之前,强烈建议您查看随附的 演示演示,以便了解预期的输出。

一些菜单和一些特殊的快捷键安排,以演示 InitThreads 项目 的实现。

InitThreadsInfo

  • 文件->新建 Ctrl + N - 在随机位置,以随机速度和随机颜色创建新的球。
  • 文件->删除 Ctrl + D - 删除选定的球。
  • 文件->重置 Ctrl + R - 删除所有球。
  • 文件->退出 Alt + F4 - 退出应用程序。
  • 编辑->全不选 Ctrl + U - 如果有选定的球,则取消全部选定。
  • 编辑->自由球 Ctrl + F - 球透明地穿过彼此移动。
  • 编辑->擦肩球 Ctrl + G - 球是实心的,不会重叠。
  • 编辑->碰撞球 Ctrl + B - 球弹性碰撞。
  • 视图->XOR Ctrl + X - 设置/重置 XOR 重叠。
  • 视图->工具栏 - 显示/隐藏工具栏。
  • 视图->状态栏 - 显示/隐藏状态栏。
  • 帮助->帮助 F1 - 显示帮助对话框。
  • 帮助->关于 InitThreads - 显示关于对话框。
  • 鼠标左键按下 - 如果碰到球,则选择/取消选择球;否则,在光标位置创建一个具有随机速度和随机颜色的新球。

以上所有命令也可在 帮助对话框 中找到,只需按下 F1 键。

Help Dialog Box

帮助对话框 是非模态的,因此您可以选择列表控件中的任何命令,然后按 确定 按钮或双击鼠标左键。

关于对话框 中,提到了 Arman S. 的原始文章。

About Dialog Box

构建说明。

解决方案配置必须安装为 Release,平台为 Win32

提供的项目是使用 MSVS-2010 的工具在 MSVS-2015 pro 中开发的。因此,exe 文件即使对于 Windows-XP 也有效。如果您不需要在 Windows-XP 中运行该应用程序,只需将工具更改为 MSVS-2015 即可。

默认编码属性为 UNICODE;尽管 MBCS 编码也可用,只需更改属性即可。

即使您是第一次使用 MSVS,只需选择菜单 调试->开始(不调试),程序 InitThreads.exe 应该就可以开始构建和运行。

项目源文件和数据存储

InitThreadsproj 路径下的标准源代码是使用标准的 MFC 应用程序向导 创建的。

  • InitThreads.cpp, InitThreadsDoc.cpp - 定义了应用程序的标准类行为。
  • InitThreadViewcpp - 标准 CView 类的实现;作者使用标准的 MFC 应用程序向导 程序创建的消息处理程序。
  • BallThread.cpp - 派生自标准的 MFC CWinThread 类,由作者重写。
  • InitThreads.rcresource.h - 作者使用标准的 资源向导 程序创建的菜单、对话框、加速器资源。

代码解释

所有菜单和加速器命令都是使用标准的 MFC AppWizard 技术完成的。因此,我觉得没有什么比标准教程讲解得更好了。解决方案的关键点:

  1. 从标准的 MFC CWinThread 类派生新的 CBallThread 类。

     class CBallThread : public CWinThread
    {
    	DECLARE_DYNCREATE(CBallThread)
    protected:
    	CBallThread();                    // protected constructor used by dynamic creation
    public:
    	virtual ~CBallThread();  
    public:
    	virtual BOOL InitInstance();
    	virtual int ExitInstance();
    	CPoint point;                     //initial point were ball born
    	CInitThreadsView * m_pView;       //reference to the main View
    	float x,y;                        //Position of the center of the ball 
                                          //from the top left corner
            float vx, vy;                 //Velocity ( number of pixels passed in one step)
    	int m_nCount;                     //Item No
    	BOOL m_bSelected;                 //Selection flag
    	COLORREF m_color;                 //Colour of the ball			
            void InitBall(CPoint point);  //Ball initialization in the point designated
    	UINT ThreadStep(void);            //One step moving
    	void DrawMyBall(CDC * pDC);	      //Draw Ball into graphics context designated	
    	int Intersection( CBallThread * pB);   //Intersection with another ball
    protected:
    	DECLARE_MESSAGE_MAP()
    public:
    	virtual BOOL OnIdle(LONG lCount);
    }; 
  2. 在指定点(如果使用了菜单 文件->新建 命令,则为随机位置;如果按下了鼠标左键,则为光标位置)启动新的 CBallThread

     void CInitThreadsView::StartBallThread(CPoint point)
    {
    	//New Thread Created:
        CBallThread *pThread = (CBallThread *)AfxBeginThread(RUNTIME_CLASS(CBallThread),  
                                THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
    	pThread->point = point;          //designated point
    	pThread->m_pView = this;         //Reference to the main View
        pThread->m_bAutoDelete = FALSE;  //Specifies whether the CWinThread object should be 
                                         //automatically deleted at thread termination.
        pThread->ResumeThread();         //New Thread Started
        m_BallList.AddTail(pThread);     //Append the New Thread tob the object list
    } 
  3. 初始化后,每个线程都在其自己的生命周期中移动。

    UINT CBallThread::ThreadStep(void)
    {
    	InitBall(point);
    	while (WAIT_TIMEOUT == ::WaitForSingleObject(g_eventEnd, 0))// Ball's life loop
    	{
    		CRect rectClient;
    		m_pView->GetClientRect(rectClient);           //Rectangle of the main View
    		//define the Rectangle containing ellipse:
            CRect rectEllipse(Round(x) - m_pView->r, Round(y) - m_pView->r, 
                                         Round(x) + m_pView->r, Round(y) + m_pView->r);
    		int xt = rectEllipse.left - rectClient.left;  //is the ellipse outside 
                                                          //from the left
    		if (xt <=0 ){vx *= -1; x -= xt;}
    		else
    		{ xt = rectClient.right - rectEllipse.right;  //is the ellipse outside 
                                                          //from the right
    		if (xt <=0 ){vx *= -1;x += xt; }
    		}
    		int yt = rectEllipse.top - rectClient.top;    //is the ellipse outside 
                                                          //from the top
    		if (yt <=0 ){vy *= -1; y -= yt;}
    		else
    		{yt = rectClient.bottom - rectEllipse.bottom; //is the ellipse outside 
                                                          //from the bottom
    		if (yt <=0 ){vy *= -1; y += yt;}
    		}
    
             POSITION psn = m_pView->m_BallList.Find(this);
             if(psn == NULL){ExitInstance(); Delete();}  //If the ball is not in list, 
                                                         //it must be dead
               else
            if(m_pView->m_bBumpingBalls || m_pView->m_bSolidBalls )//if the balls see 
                                                                   //each other
            {m_pView->m_BallList.GetNext(psn);
             for(POSITION pos = psn; pos != NULL;)       //check the intersection with 
                                                         //all the balls after  
                                                         //current in list
    	     {
    		    CBallThread * ptr = (CBallThread *)m_pView->m_BallList.GetNext(pos);
    		    Intersection(ptr);
    	      }
            }
             x+=vx;  y+=vy;                              //move into one step
    		Sleep(m_pView->m_nTick);                     //wait for time of the main timer
         }
    	return 0;
    }
  4. 主视图中所有球的完整场景以 m_nTick 毫秒间隔执行。

        void CInitThreadsView::DrawScene(void)
    {
    	CRect rect;                 //main View rectangle
    	GetClientRect(&rect);   
    	//Clear memory graphics context:
    	dcMem.BitBlt(0, 0, rect.Width(), rect.Height(), NULL, 0, 0, WHITENESS);
    	//Install ROP2 overlapping mode designated:
    	int R2mode = m_bXOR ? dcMem.SetROP2(R2_NOTXORPEN) : dcMem.GetROP2();
    	for(POSITION pos = m_BallList.GetHeadPosition(); pos != NULL;)
    	{
    		CBallThread * ptr = (CBallThread *)m_BallList.GetNext(pos);  
    		ptr->DrawMyBall(&dcMem);
    	}
         dcMem.SetROP2(R2mode);
    	 //copy memory graphics context to the main View:
    	GetDC()->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
    }    

至于内部线程的行为,我认为最好参考 Arman S.文章

使用提供的项目开发您自己的应用程序。

您可以全部采用这个项目,将其重命名为我的以前的 CodeProject 文章“一键从现有代码创建 MFC 项目”中的项目,并随意组合和改进代码。

如果您引用了我的代码,将非常感激。

兴趣点和致谢

如果您更改了球的交叉状态,您可以看到不同的球移动画面。因此,擦肩球看起来如下:

InitThreadsClance

从这篇首次发表的文章的一些评论中,我发现有些人对 MFC 的性能不满意。特别是对于那些对 MFC 感到不满的人,我开发了具有相同功能的 Win32 版本项目。在下面的示例中,Win32 版本允许球重叠,并获得了类似碰撞球的速度。

Win32Threads

值得注意的是,本文及代码并非关于图形和球的碰撞。其思想是创建与主程序并行运行的线程对象。

我相信这个项目对于刚开始学习多线程技术的读者会很有趣。

非常感谢 Arman S. 的出色工作。另外,我的孙女喜欢玩彩泡泡。

历史

  • 2021 年 10 月 7 日:初始版本
  • 2021 年 10 月 17 日:添加了 Win32 项目,并对 MFC 项目进行了一些更正。

 

© . All rights reserved.