在 MFC 和 Win32 中初始化线程





5.00/5 (18投票s)
在 MFC 和 Win32 中启动线程以及一些处理示例
MFC
Win32
引言
在我的一些项目开发中,多线程技术变得很有用。一如既往,我查看了 CodePoject 并找到了 Arman S. 的精彩文章 《MFC 多线程应用程序中的同步》。文章的思路和脚本代码都很好。这是熟悉多线程的良好起点。
出于习惯,演示实现的效果并不好。在绘制新椭圆之前,用白色矩形绘制旧椭圆位置是一个糟糕的想法。当椭圆重叠时,会出现可见的白色间隙。
因此,我冒昧地通过标准的 MSVS AppWizard 程序改进了性能,并添加了一些处理功能。
背景
上述文章写于 2007 年,因此提供的 WorkerThreads
项目与最新版本的 MSVS 不直接兼容。因此,我创建了一个新的、单文档项目,名为 WorkerThreads
,使用 MSVS-2012 pro,将 WorkerThreadsView.h 和 WorkerThreadsView.cpp 的代码替换为 Arman S. 项目中的相同代码,并从相同的源文件中包含了 Thread.h 和 Thread.cpp。编译产生的可执行文件产生了如下视频演示:
为了改进上述性能,我开发了一个新项目 InitThreads
。可执行文件 InitThreads.exe 是使用 MSVS-2012 pro 构建的。
演示说明
在开始构建提供的项目之前,强烈建议您查看随附的 演示演示,以便了解预期的输出。
一些菜单和一些特殊的快捷键安排,以演示 InitThreads 项目 的实现。
- 文件->新建 Ctrl + N - 在随机位置,以随机速度和随机颜色创建新的球。
- 文件->删除 Ctrl + D - 删除选定的球。
- 文件->重置 Ctrl + R - 删除所有球。
- 文件->退出 Alt + F4 - 退出应用程序。
- 编辑->全不选 Ctrl + U - 如果有选定的球,则取消全部选定。
- 编辑->自由球 Ctrl + F - 球透明地穿过彼此移动。
- 编辑->擦肩球 Ctrl + G - 球是实心的,不会重叠。
- 编辑->碰撞球 Ctrl + B - 球弹性碰撞。
- 视图->XOR Ctrl + X - 设置/重置 XOR 重叠。
- 视图->工具栏 - 显示/隐藏工具栏。
- 视图->状态栏 - 显示/隐藏状态栏。
- 帮助->帮助 F1 - 显示帮助对话框。
- 帮助->关于 InitThreads - 显示关于对话框。
- 鼠标左键按下 - 如果碰到球,则选择/取消选择球;否则,在光标位置创建一个具有随机速度和随机颜色的新球。
以上所有命令也可在 帮助对话框 中找到,只需按下 F1 键。
帮助对话框 是非模态的,因此您可以选择列表控件中的任何命令,然后按 确定 按钮或双击鼠标左键。
在 关于对话框 中,提到了 Arman S. 的原始文章。
构建说明。
解决方案配置必须安装为 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.rc 和 resource.h - 作者使用标准的 资源向导 程序创建的菜单、对话框、加速器资源。
代码解释
所有菜单和加速器命令都是使用标准的 MFC AppWizard 技术完成的。因此,我觉得没有什么比标准教程讲解得更好了。解决方案的关键点:
-
从标准的 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); };
-
在指定点(如果使用了菜单 文件->新建 命令,则为随机位置;如果按下了鼠标左键,则为光标位置)启动新的
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 }
-
初始化后,每个线程都在其自己的生命周期中移动。
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; }
-
主视图中所有球的完整场景以
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 项目”中的项目,并随意组合和改进代码。
如果您引用了我的代码,将非常感激。
兴趣点和致谢
如果您更改了球的交叉状态,您可以看到不同的球移动画面。因此,擦肩球看起来如下:
从这篇首次发表的文章的一些评论中,我发现有些人对 MFC 的性能不满意。特别是对于那些对 MFC 感到不满的人,我开发了具有相同功能的 Win32 版本项目。在下面的示例中,Win32 版本允许球重叠,并获得了类似碰撞球的速度。
值得注意的是,本文及代码并非关于图形和球的碰撞。其思想是创建与主程序并行运行的线程对象。
我相信这个项目对于刚开始学习多线程技术的读者会很有趣。
非常感谢 Arman S. 的出色工作。另外,我的孙女喜欢玩彩泡泡。
历史
- 2021 年 10 月 7 日:初始版本
- 2021 年 10 月 17 日:添加了 Win32 项目,并对 MFC 项目进行了一些更正。