最近正在忙着开发一款三维CAD系统。BOSS希望该CAD系统具备文档自动保存功能,就像Office系列软件一样,在用户不知不觉的情况下将其所作修改进行保存,这样可以防止在意外宕机情况下用户的操作丢失。
其实添加自动保存功能并不是一件非常困难的事情,最简单的情况就是设置Timer计时器,捕捉系统消息WM_TIMER,并在Timer的响应函数里调用CDocument::OnSaveDocument()函数,使用主线程进行文档保存。不过,这样做是有弊端的。例如在我们所要做的CAD系统中,在实际使用过程中,可能会出现数据量非常庞大的零部件或组装结构。如果在这种情况下使用主线程来保存文档,文档的保存时间可能会较长。而在文档保存的过程中,系统会将光标设置为等待,因此,不仅无法再对文档进行操作,即使能操作,也会因为主线程正在忙碌状态而使系统无法响应用户操作,势必影响使用。
一个较好的方法就是使用辅助工作线程来进行文档的保存工作。由于工作线程只在后台运行,与主线程相对独立,因此,可以较好地解决用户操作与文档保存的并行问题。虽然在单核CPU情况下,所谓的多线程仍然存在CPU使用上的等待与交替过程,但由系统强制分配的CPU使用时间保证了两种操作形式上的并行,基本上解决了问题。
另外需要强调的是,由于MFC类库中的CDocument类在使用过程中,其内部函数与主线程存在线程依赖关系。具体是由一个Map影射造成的,因此如果使用传递给工作线程处理函数的CDocument类型指针直接调用CDocument::OnSaveDocument()则会造成程序断言失败而意外退出。下面是windows核心代码wincore.cpp内部包含的一个原函数。
{
if (m_hWnd == NULL)
return; // null (unattached) windows are valid
// check for special wnd??? values
ASSERT(HWND_TOP == NULL); // same as desktop
if (m_hWnd == HWND_BOTTOM)
ASSERT(this == &CWnd::wndBottom);
else if (m_hWnd == HWND_TOPMOST)
ASSERT(this == &CWnd::wndTopMost);
else if (m_hWnd == HWND_NOTOPMOST)
ASSERT(this == &CWnd::wndNoTopMost);
else
{
// should be a normal window
ASSERT(::IsWindow(m_hWnd));
// should also be in the permanent or temporary handle map
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL);
CObject* p;
ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
(p = pMap->LookupTemporary(m_hWnd)) != NULL);
ASSERT((CWnd*)p == this); // must be us
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
}
}
注意看上述代码的注释部分。由于CDocument::OnSaveDocument()函数进行了较高层封装,与线程相关,因此,CDocument::OnSaveDocument()函数需要重写。在重写的过程中,不能再继承CDocument::OnSaveDocument()函数,而需要从更低层着手写保存操作。
关于辅助工作线程的使用方法,我会在稍后的时间里贴出程序代码和注释,敬请关注!