【发布时间】:2017-07-05 05:21:51
【问题描述】:
如标题所示,我想将项目添加/删除到从工作线程的 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 时,它才能在工作线程中工作,但我会避免这种情况,因为我想使用此列表控件中的多个实例,并且我更喜欢任何比全局变量更优雅的方式。
我想让这个类可重用,因为我想通过一些修改(更多列,不同颜色)多次使用它。
感谢任何帮助和想法,让我可以完成这项工作。
环境: 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);
【问题讨论】:
标签: c++ multithreading user-interface wtl