没有合适的资源?快使用搜索试试~ 我知道了~
VC++知识集:收录了若干程序员在进行VC++编程时遇到的实际问题,并给出了详细的讲解。希望对您有所帮助!
资源详情
资源评论
资源推荐
How to use the same thread function for multiple threads (safely)
Introduction
My recent forray into threads and multithreaded applications has taught me a few
lessons which I thought I'd pass on. As I suspect that most people know this already
(it was just me taking a while to catch up), the target audience is beginners to the
world of multithreading. If you're not a beginner, then please read this anyway and
let me know if I've got it all wrong!
The Problem
My problem was that I wanted several threads to basically do the same thing. Being
the OO developer that I am I wanted to be able to wrap this up in a class and just
create several instances of that class and Bob's your uncle. Unfortunately, the
function that you pass to
AfxBeginThread must be either declared outside a class
or be a static class member function. Being OO-type people who want to try to
reduce namespace pollution we'll be using the static class member function
approach, unfortunately this means that all of our class instances will be using the
same function, which is exactly what I'm trying to avoid.
Racing for the Handle
By way of an introduction I'll start off with a brief description of what
AfxBeginThread does. Essentially AfxBeginThread is a helper function that
handles setting up and starting a
CWinThread object for us. To do this it goes
through (broadly speaking) four steps:
1. Allocate a new
CWinThread object on the heap.
2. Call
CWinThread::CreateThread() setting it to initially start suspended.
3. Set the thread's priority.
4. Call
CWinThread::ResumeThread().
The default usage of
CWinThread and AfxBeginThread has a slight problem. By
default, as soon as the thread has completed it is deleted and so if our thread
doesn't take much time to complete, by the time that
AfxBeginThread returns our
CWinThread pointer may already have been deleted and so dereferencing this
pointer to get at the thread handle for a
WaitForSingleObject() call will cause
our application to crash.
Fortunately
CWinThread provides a simple answer to this by way of the
m_bAutoDelete member variable. As described above, this is TRUE by default.
However, if we set it to FALSE, then the thread will not delete itself when it
completes. This does have the same handle racing problem described above,
because we can't access the member until
AfxBeginThread returns, by which
time it could already have been deleted! The answer is not to use the default
AfxBeginThread and to create the thread initially suspended:
Collapse Copy Code
CWinThread* pThread = AfxBeginThread (ThreadFunc, lpVoid,
THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
ASSERT (NULL != pThread);
pThread->m_bAutoDelete = FALSE;
pThread->ResumeThread ();
Obviously, this does entail a little extra handling ourselves, but we're big boys and
girls now aren't we!
The Solution
The solution to our problem isn't too complex and doesn't require too much work.
Essentially, we are going to derive a class from
CWinThread with a static member
thread function, but this thread function will call a non-static member function to do
the main guts of what our thread is supposed to do. It will also set the
m_bAutoDelete member to FALSE in the constructor to overcome the handle
racing problem.
Basically our class will look like this:
Collapse Copy Code
class CThreadEx : public CWinThread {
public:
CMyThread (AFX_THREADPROC pfnThreadProc);
static UINT ThreadFunc (LPVOID lpParam);
protected:
virtual UINT Execute ();
};
CThreadEx::CThreadEx (AFX_THREADPROC pfnThreadProc) : CWinThread (pfnThreadProc,
NULL) {
m_bAutoDelete = FALSE;
// Undocumented variable. Need to set the thread parameters variable to
this
m_pThreadParams = this;
}
// Static
UINT CThreadEx::ThreadFunc (LPVOID lpParam) {
ASSERT (NULL != lpParam);
CThreadEx* pThread = (CThreadEx*)lpParam;
return pThread->Execute ();
}
UINT CThreadEx::Execute () {
// Do something useful ...
// Return a value to indicate success/failure
return 0;
}
Those of you with a keen eye will have noticed that the constructor for CThreadEx
uses an initialiser list to call a
CWinThread constructor that isn't documented. If
you look in "thrdcore.cpp", you'll find a constructor that looks like this:
Collapse Copy Code
CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam)
{
m_pfnThreadProc = pfnThreadProc;
m_pThreadParams = pParam;
CommonConstruct();
}
This introduces us to two undocumented features, firstly the constructor itself, and
secondly the member variables
m_pfnThreadProc and m_pfnThreadParams.
Also in "thrdcore.cpp" is the source for
AfxBeginThread itself. The line below
shows the use of the above constructor:
Collapse Copy Code
CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
This is a rather obscure ommission to have made, but we live and learn. The two
parameters to this constructor are clearly the thread function and the parameter to
be passed to the thread function. In the class outlined above the parameter to the
thread function needs to be a pointer to the class instance, so our constructor sets
m_pThreadParams = this.
Implementation
You have two options when it comes to implementing this solution. If you only
require a one off solution, then use the above class as a template and build your
functionality into it. If you think that this is something you'll be doing quite a lot,
then define the above class as your base class and inherit from it putting your
functionality in the derived class.
Using it all looks something like this:
Collapse Copy Code
CThreadEx* pThread = new CThreadEx (CThreadEx::ThreadFunc);
VERIFY (pThread->CreateThread());
...
WaitForSingleObject (pThread->m_hThread, INFINITE);
delete pThread;
The thread can be initially started suspended by specifying CREATE_SUSPENDED as
the first parameter to the
CreateThread() call if this functionality is required. Also,
if you need to use a thread priority other than
THREAD_PRIORITY_NORMAL just call
SetThreadPriority() after CreateThread().
Conclusion
And there you have it ... a thread class that allows multiple instances to manipulate
their own member data, etc. without having to have a separate thread function for
each one. I hope this helps someone out there, it certainly helped me.
License
[转]CWnd中PreCreateWindow、PreSubclassWindow、SubclassWindow的区别
MFC(VC6.0)的CWnd及其子类中,有如下三个函数:
class CWnd : public CCmdTarget
{
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual void PreSubclassWindow();
BOOL SubclassWindow(HWND hWnd);
};
让人很不容易区分,不知道它们究竟干了些什么,在什么情况下要改写哪个函数?
想知道改写函数?让我先告诉你哪个不能改写,那就是 SubclassWindow。Scott Meyers 的杰作<<Effective C++>>
的第 36 条是这样的 Differentiate between inheritance of interface and inheritance of implementation. 看了后你马
上就知道,父类中的非虚拟函数是设计成不被子类改写的。根据有无 virtual 关键字,我们在排除了 SubclassWindow 后,也
就知道 PreCreateWindow 和 PreSubClassWindow 是被设计成可改写的。接着的问题便是该在什么时候该写了。要知道什
么时候该写,必须知道函数是在什么时候被调用,还有执行函数的想要达到的目的。我们先看看对这三个函数,MSDN 给的解
释:
PreCreateWindow:
Called by the framework before the creation of the Windows window
attached to this CWnd object.
(译:在窗口被创建并 attach 到 this 指针所指的 CWnd 对象之前,被 framework 调用)
PreSubclassWindow:
This member function is called by the framework to allow other necessary
subclassing to occur before the window is subclassed.
(译:在 window 被 subclassed 之前被 framework 调用,用来允许其它必要的 subclassing 发生)
虽然我已有译文,但还是让我对 CWnd 的 attach 和窗口的 subclass 作简单的解释吧!要理解 attach,我们必须要知道一个
C++的 CWnd 对象和窗口(window)的区别:window 就是实在的窗口,而 CWnd 就是 MFC 用类对 window 所进行 C++封
装。attach,就是把窗口附加到 CWnd 对象上操作。附加(attach)完成后,CWnd 对象才和窗口发生了联系。窗口的 subclass
是指修改窗口过程的操作,而不是面向对象中的派生子类。
好了,PreCreateWindow 由 framework 在窗口创建前被调用,函数名也说明了这一点,Pre 应该是 previous 的缩写,
剩余1863页未读,继续阅读
yimu1986
- 粉丝: 0
- 资源: 18
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0