MFC多线程(22)

Posted 易老师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MFC多线程(22)相关的知识,希望对你有一定的参考价值。

可以把线程看成是操作系统分配CPU时间的基本实体。系统为每一个线程分配一个CPU时间片(20毫秒左右),不停地在各个线程之间切换,某个线程只有在分配的时间片内才有对CPU的控制权。由于系统为每个线程划分的时间片很小,所以看上去好象是多个线程在同时运行。进程中的所有线程共享进程的虚拟地址空间,这意味着所有线程都可以访问进程的全局变量和资源。

MFC多线程

C++有几种开启多线程的方法,MFC中利用 AfxBeginThread 来实现多线程的创建。调用方式有两种:

1、CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

2、CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

简而言之,第一种利用的是绑定线程函数,应用于工作线程,用于后台计算等(不带窗口),第二种则可以拥有自己的线程窗口,实现消息处理。

一、工作线程:

首先,定义线程函数,可以是全局函数或者静态函数,函数原型:

UINT Proc  (LPVOID lp)           //  lp 用于开启线程时,参数的传递

然后,开启线程:

::AfxBeginThread(Proc  ,(LPVOID)&lpData,NULL,NULL,NULL,NULL);

其中,lpData 用于传递给线程函数的参数, AfxBeginThread 创建线程后的返回值为CWinThread 的指针,可以利用该指针对线程进行一些处理(不推荐,除非有线程同步机制)。

与函数调用的最大不同是:函数调用是一种阻塞式的方式,也就是主调方需要等待被调函数的返回,而线程则是一种非阻塞的工作模式,开启新的工作线程后就执行后面的流程了。

在窗口程序中,往往将有大量耗时工作的任务交付给工作线程,这样主线程就不会被阻塞,可以及时处理用户的交互功能,避免 “卡死” 的现象。

开启的工作线程,可以从主线程中获取参数 LPVOID  ,由于线程共享进程所有的资源,所以可以按约定进行各种强制转换。

工作线程启动后,将自动执行线程函数(除非创建的时候指明需要手动),线程函数执行完毕后线程就终止了。

二、窗口线程

与工作线程不同的是,创建的新线程拥有自己的窗口。

首先,生成两个类,一个是继承于 CFrameWnd 的窗口类,另外一个是继承于CWinThread 的线程类

CMyThreadWnd 头文件:

#pragma once
#include "afxwin.h"

#define WM_TEST WM_USER+1   //自定义消息,用于测试消息响应
class CMyThreadWnd :
	public CFrameWnd

	
public:
	CMyThreadWnd(void);
	~CMyThreadWnd(void);
	
	DECLARE_MESSAGE_MAP()
	afx_msg LRESULT OnTest(WPARAM wParam,LPARAM lParam);
;

 CMyThreadWnd  CPP文件:

#include "stdafx.h"
#include "MyThreadWnd.h"


CMyThreadWnd::CMyThreadWnd(void)




CMyThreadWnd::~CMyThreadWnd(void)


BEGIN_MESSAGE_MAP(CMyThreadWnd, CFrameWnd)
	ON_MESSAGE(WM_TEST,OnTest)	
END_MESSAGE_MAP()


LRESULT CMyThreadWnd::OnTest(WPARAM wParam,LPARAM lParam)

	LPCSTR p=(LPCSTR)wParam;
	CString data=(CString)p;
	CDC*dc = this->GetDC();
	CRect rc;
	this->GetClientRect(&rc);
	dc->PatBlt(0,0,rc.right,rc.bottom,WHITENESS);
	dc->TextOut(100,100,data);
	this->ReleaseDC(dc);
	return 0;

PS:消息循环的三个宏

DECLARE_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyThreadWnd, CFrameWnd)
END_MESSAGE_MAP() 

可以利用VS中类向导自动添加 (随便加一个消息处理函数就有了)

CMyThread  头文件:  

#pragma once
#include "afxwin.h"
#include "MyThreadWnd.h"
class CMyThread :
	public CWinThread

	DECLARE_DYNCREATE(CMyThread)
protected:
	CMyThreadWnd *wnd;
public:
	CMyThread(void);
	~CMyThread(void);
	virtual BOOL InitInstance();
	DECLARE_MESSAGE_MAP()
	afx_msg void OnTest(WPARAM wParam,LPARAM lParam);
;

动态创建宏,手动添加(否则报内存不够的错误) DECLARE_DYNCREATE

CMyThread  CPP文件: 

#include "stdafx.h"
#include "MyThread.h"


IMPLEMENT_DYNCREATE(CMyThread, CWinThread);
CMyThread::CMyThread(void)

	wnd = new CMyThreadWnd();



CMyThread::~CMyThread(void)




BOOL CMyThread::InitInstance()

	// TODO: 在此添加专用代码和/或调用基类
	 CWinThread::InitInstance();
	wnd->Create(NULL,"我的线程窗口");
	wnd->ShowWindow(SW_SHOW);
	wnd->UpdateWindow();
	return TRUE;

BEGIN_MESSAGE_MAP(CMyThread, CWinThread)
	ON_THREAD_MESSAGE(WM_TEST,OnTest)
END_MESSAGE_MAP()


void CMyThread::OnTest(WPARAM wParam,LPARAM lParam)

	LPCSTR p=(LPCSTR)wParam;
	CString s;
	s.Format("获取消息:%s",p);
	::AfxMessageBox(s);
	::PostMessage(wnd->m_hWnd,WM_TEST,wParam,lParam);

cpp文件中,IMPLEMENT_DYNCREATE    与头文件中动态创建相对应。

重写InitInstance 函数,该函数在创建线程时自动调用,返回值必须为TRUE,否则线程终止。在该函数中创建线程窗口,显示并更新窗口。OnTest函数用于响应主线程发送的 WM_TEST 消息,并且把该消息再次转发给 窗口。

调用方法,参考代码:

CWinThread *t=NULL;
t = ::AfxBeginThread(RUNTIME_CLASS(CMyThread));
LPCSTR data="Test Message";
PostThreadMessage(t->m_nThreadID,WM_TEST,(WPARAM)data,0);

由此可见,窗口线程与工作线程的区别。

然而!

工作线程也是可以拥有窗口的,不过该窗口会随工作线程的终止而销毁,另外,工作线程同样也可以处理消息。参考测试方法:

线程函数:

UINT Proc(LPVOID lp)
 
	CMyWnd* Dlg = new CMyWnd();
     Dlg->Create(NULL, "线程窗口");
     Dlg->ShowWindow(SW_SHOW);
     Dlg->UpdateWindow();
	 MSG msg;
	 
	 while(::GetMessage(&msg,0,0,0))
	 
		 if(msg.message == WM_LBUTTONDBLCLK)
		 
			 ::SendMessage(Dlg->m_hWnd,WM_LBUTTONDBLCLK,0,0);
			 continue;
		 
		 TranslateMessage(&msg);
		 DispatchMessage(&msg);
	 
	
	return 0;

测试代码

  CWinThread* th3=::AfxBeginThread(abc3,NULL,NULL,NULL,NULL,NULL);
  ::PostThreadMessage(th3->m_nThreadID,WM_LBUTTONDBLCLK,0,0);

  需要通过SendMessage给窗口发消息(不进消息队列)

当然,以上方法比窗口线程方法复杂多了!

在多线程崩溃的 MFC 中使用 Techart activeX

【中文标题】在多线程崩溃的 MFC 中使用 Techart activeX【英文标题】:using Teechart activeX in MFC with multithreading crashed 【发布时间】:2016-04-14 08:16:25 【问题描述】:

当我在MFC中使用teechart显示用户线程中的曲线时,它崩溃了。我想知道这个activeX是否可以支持多线程。

【问题讨论】:

【参考方案1】:

TeeChart 不是线程安全的,因此您应该防止图表从一个线程重新绘制并同时从另一个线程修改。

【讨论】:

非常感谢。我找不到在多线程中使用 Techart 的演示。 @耶雷

以上是关于MFC多线程(22)的主要内容,如果未能解决你的问题,请参考以下文章

MFC 对话框错误中的 C++ 多线程

mfc如何使用多线程搜索文件夹?

c++/mfc 子线程结束后再来执行主线程下操作 该怎么处理

MFC多线程安全问题。

MFC多线程内存泄漏问题&解决方法

MFC 多线程:AfxBeginThread 与 Boost.Thread?