【问题标题】:MFC Open big documentMFC 打开大文档
【发布时间】:2015-05-25 10:57:37
【问题描述】:

我有一个单文档接口 (SDI) Microsoft 基础类 (MFC) 应用程序,它必须加载一个大文档文件(大约需要 2 分钟)。因此,当我打开文档时,我的应用程序一直没有响应。

但是,我希望我的应用在打开文档时能够做出响应。问题是,如果我尝试在线程上加载我的文档,我的 OnopenDocument 函数(在我的文档中)将在我实际打开文档之前返回:

BOOL CmodguiDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
  if (!CDocument::OnOpenDocument(lpszPathName))
    return FALSE;
  start_tread(_open_doc); // just an example
  return TRUE; // Will return before the document will actually be open
}

我怎样才能做到这一点是非锁定的,但只有在文档实际打开后才返回?或者,在加载文档时,如何至少让我的应用程序响应?

谢谢

【问题讨论】:

  • 你不能希望一个线程同时阻塞并继续执行。除非你买一台昂贵的量子计算机。
  • @IInspectable 这就是我要求替代架构的原因
  • 另一种方法是将文件名存储在您的OnOpenDocument 回调中并按需加载数据。您不能在 GUI 线程上等待加载操作运行完成并同时为其他消息提供服务。在加载文档之前,您必须以一种或另一种方式返回。
  • 如果您的应用程序准备好处理重入问题,您还可以从文档加载器定期调用CWinThread::PumpMessage,以在 GUI 线程上处理消息。

标签: c++ mfc


【解决方案1】:

创建线程后返回即可。您应该为文档定义一个状态,例如loadingloaded。当你启动线程时,状态应该是loading。视图应该查看状态并相应地显示。当线程完成加载文档时,它应该向文档发布一条消息。在处理程序中,将状态设置为 loaded 并调用 UpdateAllViews() 以使视图有机会使用新的文档数据进行更新。

示例:这将在文档加载时打印“正在加载”,并在文档在视图中完成后“加载”和加载。

在resource.h中:

#define IDD_NotifyDocumentFinished        101

在文档标题中:

public:
   enum DocState
   {
      None,
      Failed,
      Loading,
      Loaded
   };

   DocState GetDocState() const {return m_state;}
private:
   DocState m_state;
   void StartLoading();

在文档实现中:

BOOL CMFCDocViewAsyncDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
   if(!CDocument::OnOpenDocument(lpszPathName))
      return FALSE;   

   m_state = Loading;

   StartLoading();

   return TRUE;
}

UINT LongRunningFunction(LPVOID param)
{
   Sleep(3000);

   HWND hWnd = AfxGetApp()->m_pMainWnd->GetSafeHwnd();

   NMHDR hdr = {hWnd, IDD_NotifyDocumentFinished, 0};
   ::SendMessage(hWnd, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&hdr));

   return 0;
}

void CMFCDocViewAsyncDoc::StartLoading()
{
   AfxBeginThread(&LongRunningFunction, nullptr);
}

BOOL CMFCDocViewAsyncDoc::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
   if(HIWORD(nCode) == WM_NOTIFY)
   {
      WORD wCode = LOWORD(nCode);
      AFX_NOTIFY * notify = reinterpret_cast<AFX_NOTIFY*>(pExtra);
      if(notify->pNMHDR->idFrom == IDD_NotifyDocumentFinished)
      {
         m_state = Loaded;
         UpdateAllViews(nullptr);
      }
   }

   return TRUE;
}

在视图中:

void CMFCDocViewAsyncView::OnDraw(CDC* pDC)
{
    CMFCDocViewAsyncDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    CMFCDocViewAsyncDoc::DocState state = pDoc->GetDocState();
   CString sstate;
   switch(state)
   {
   case CMFCDocViewAsyncDoc::None:
      sstate = "None";
      break;
   case CMFCDocViewAsyncDoc::Failed:
      sstate = "Failed";
      break;
   case CMFCDocViewAsyncDoc::Loading:
      sstate = "Loading";
      break;
   case CMFCDocViewAsyncDoc::Loaded:
      sstate = "Loaded";
      break;
   }
   pDC->TextOut(50, 50, sstate);
}

更新:也可以在此处查找类似的更详细示例 http://www.codeproject.com/Articles/14706/Notifying-the-Document

【讨论】:

  • 谢谢,但你能说得更具体点吗?如何定义文档状态?
  • 还有另一个问题。我无法从线程获取文档句柄,我无法从线程更新状态
  • 不是来自线程,而是在线程完成后发布的消息的处理程序中。
  • 还有一个问题,文档数据集是什么时候?例如带有项目名称的窗口标题?
【解决方案2】:

如果只加载一个东西就需要两分钟,那么有两个原因:

  1. 加载时您正在做一些愚蠢的事情,例如在数据上运行昂贵的算法,如索引或图形渲染。如果将数据存储在容器中,分配的开销也会变得很大。如果是这种情况,请仅按需执行它们或在后台异步执行这些操作。例如,您可以在完成后通知主线程(运行 UI 的线程)以显示渲染的图形。
  2. 数据非常庞大。在这种情况下,您将不得不重新考虑如何访问这些数据,因为任何操作都可能很昂贵。您最好的选择是使用异步模型,例如向模型发送请求以执行某些操作。然后模型向 UI 发送响应以显示数据。在这种情况下,您需要考虑如何使这对用户透明。一种方法是在更改请求后使显示区域变暗,并仅在响应或进度条后将其变亮。

【讨论】:

  • 我的项目文件由一个唯一的文件组成,最大可以增长到 2gb
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多