- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如标题所示,我想从工作线程向从 WTL CListViewCtrl 类派生的类添加/删除项目,但总是得到“抛出未处理的异常:读取访问冲突。”
我尝试了 Win32 API PostMessage 和 SendMessage 但是一旦工作线程接触到 CListViewCtrl 的 HWND,我就会得到同样的异常。
// CListCtrl member function, calling from worker thread
HWND GetHwnd()
{
return hwndListCtrl; // exception here
}
我试过这个 SafeQueue但是一旦工作线程接触到互斥量或队列,就会再次发生异常。
// SafeQueue is member variable in CListViewCtrl, created in GUI thread
SafeQueue<T> m_SafeQueue;
. . .
// member function in SafeQueue class, calling from worker thread
void enqueue(T t)
{
std::lock_guard<std::mutex> lock(m); // exception here
q->push(t);
}
我尝试使用 new 和 HeapAlloc/LocalAlloc 创建互斥锁和队列,但再次出现相同的异常。
我尝试了 Win32 API CreateMutex 但没有成功,从工作线程访问互斥锁句柄时出现同样的异常。
当我从 GUI 线程添加项目时它工作正常。
如果我将 HWND 或互斥锁和队列声明为 static/global ,它只能在工作线程中工作,但我会避免这种情况,因为我想使用此 listcontrol 中的多个实例并且我更喜欢任何比全局变量更优雅的方式。
我想让这个类可重用,因为我想通过一些修改(更多列、不同颜色)多次使用它。
我很感激任何帮助以及我如何使这项工作的想法。
环境:VS2015 社区、WTL/C++ 和 Win10 Pro 64bit
我发现导致访问冲突异常的问题:我在 CListViewCtrl 类中将 ThreadProc 回调函数声明为静态成员函数。
// DO NOT USE
// in CListViewCtrl
**static** DWORD WINAPI ThreadProc(LPVOID lp)
{
. . .
}
LRESULT OnStartWorkerThread(WORD /*wNotifyCode*/, WORD /*wID*/, HWND . ..)
{
DWORD dw;
::CreateThread(NULL, 0, this->ThreadProc, NULL, 0, &dw);
}
可行的解决方案:
class CListViewCtrl ...
{
// thread-safe queue to store listctrl items to be added later in GUI thread
SafeQueue<CListCtrlItem<nCols> > m_SafeQueue;
// thread ID of the thread in which listctrl was created, saved in OnCreate
DWORD m_dwGuiTid;
// . . .
检查是否从 GUI 或任何其他线程调用了 SafeAddItem 函数
BOOL InvokeRequired()
{
if (m_GuiTid == ::GetCurrentThreadId())
return false;
return true;
}
// ...
SafeAddItem 成员函数可以从 GUI 和工作线程调用
void SafeAddItem(CListCtrlItem<nCols> item)
{
if (!InvokeRequired())
{
// we are in GUI thread so just add listctrl item "normal" way
AddItem(item);
return;
}
// we are in other thread so enqueue listctrl item and post a message to GUI
m_SafeQueue.Enqueue(item);
::PostMessage(m_hWnd, WM_ADD_ITEM, 0, 0);
}
// . . .
PostMessage 的消息处理程序,我们在 GUI 线程中
LRESULT OnAddItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
CListCtrlItem<nCols> item;
while (!m_SafeQueue.Empty())
{
item = m_SafeQueue.Dequeue();
// we are in GUI thread so we can add list ctrl items normal way
AddItem(item);
}
return 1;
}
// ...
}
现在我们可以通过这种方式从任何线程添加 listctrl 项目。我将 this 指针传递给 _beginthreadex
中的 ThreadProcm_ListCtrl.SafeAddItem(item);
最佳答案
问题似乎与工作线程的 UI 更新无关,而是工作线程本身的正确使用。
关于进行 UI 更新的危险有足够多的评论:它们都是关于潜在的死锁问题。大多数更新涉及发送消息,这是一个阻塞 API 调用。当您从工作线程执行更新并且调用线程被阻塞时,UI 中的处理程序尝试与工作线程同步或以其他方式协作可能会导致死锁。解决此问题的唯一方法是在工作线程中准备更新并向 UI 线程发送信号(包括通过发布消息而不是发送消息,就 SendMessage
、PostMessage
API 而言) 接管并完成来自 UI 线程的更新。
回到最初的问题:您似乎遇到了静态线程过程的问题。 The fourth argument in the CreateThread
call is :
lpParameter [in, optional]
A pointer to a variable to be passed to the thread.
你有它 NULL
并且你通常使用它来将 this
值传递给你的线程过程回调。这样您就可以将执行从静态函数传回给您的类实例:
DWORD CFoo::ThreadProc()
{
// ThreadProc with proper "this" initialization
// HWND h = GetHwnd()...
}
DWORD WINAPI ThreadProc(LPVOID pvParameter)
{
return ((CFoo*) pvParameter)->ThreadProc();
}
LRESULT CFoo::OnStartWorkerThread(WORD /*wNotifyCode*/, WORD /*wID*/, HWND ...)
{
DWORD dw;
::CreateThread(NULL, 0, this->ThreadProc, (LPVOID) this, 0, &dw);
}
另请注意,您不应该直接使用 CreateThread
:您有 _beginthreadex
和 AtlCreateThread
( related question )。
关于c++ - 在 WTL 和 C++ 中从工作线程更新 CListViewCtrl,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42268177/
我在报告模式下有一个 Windows 模板库 CListViewCtrl(因此有一个包含 2 列的标题)和所有者数据集。此控件显示搜索结果。如果没有返回结果,我想在列表框区域显示一条消息,指示没有结果
这是我的代码,用于获取在我的 CListViewCtrl 中选择的项目的名称: LVITEM item = { LVIF_PARAM }; CString itemText; clistViewCtr
当添加新项目时,我想滚动到 ListView 的末尾。我正在使用 WTL 的 CListViewCtrl。这是我添加新项目和滚动的功能: void CMainDlg::addMessage(CStri
如标题所示,我想从工作线程向从 WTL CListViewCtrl 类派生的类添加/删除项目,但总是得到“抛出未处理的异常:读取访问冲突。” 我尝试了 Win32 API PostMessage 和
我是一名优秀的程序员,十分优秀!