GDI+ 多线程绘图

Posted

技术标签:

【中文标题】GDI+ 多线程绘图【英文标题】:GDI+ drawing with multithreading 【发布时间】:2013-07-16 12:51:07 【问题描述】:

免责声明:我在多线程方面有点菜鸟。我在网上读过一些东西,做过一些简单的多线程示例。

我有一个 Win32 应用程序,它想在一个线程中绘制内容并在另一个线程中处理 Win32 消息。但是,在创建窗口并启动线程后,它会挂起。我有一种预感,它可能与 WaitForMultipleObjects() 有关,但我不知道如何使它正确。有谁知道为什么会这样?我应该暂停和恢复线程吗?

这是我的代码:

WinAPI:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)

    /* initialization blah blah */

    zgE->startThreads(); // <--- starts the 2 threads

    WaitForMultipleObjects(zgE->getThreadsNo(), zgE->getThreads(), TRUE, INFINITE);
    return TRUE;

这就是我启动线程的方式:

void zgEngine::startThreads()

    /* allocation and stuff, blah blah blah */

    m_arrThreads[m_nThreads++] = CreateThread(NULL, 0, &zgEngine::handleMsg, (void*)this, NULL, NULL);
    m_arrThreads[m_nThreads++] = CreateThread(NULL, 0, &zgEngine::drawObjects, (void*)this, NULL, NULL);

    assert(m_nThreads <= THREADS_NO);

绘制和处理消息的 2 个函数非常简单。每个都有一个while循环。

// draw function
DWORD WINAPI zgEngine::drawObjects(LPVOID lpParam)
    
    while (true)
    
        /* draw stuff - valid code that if called outside this function
           works as intended  */
    

    return TRUE;


// message handler function
DWORD WINAPI zgEngine::handleMsg(LPVOID lpParam)

    MSG msg;
    while (true)
    
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        
            // Process the message
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
    

    return TRUE;

当我不使用线程并删除drawObjects()中的“while(true)”,但留下代码(只执行一次)时,不要调用handleMsg(),让WinMain像在下面的示例中,它就像一个魅力。

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)

    /* initialization blah blah */

    MSG msg;
    while (true)
    
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        
            // Process the message
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
         else 
             zgEngine::DrawObjects(zgE);
         
    
    return TRUE;

后期编辑:据我所知,PeekMessage() 总是返回 0 :(

【问题讨论】:

有传言说 Windows user32 和 gdi 是线程安全的。我持怀疑态度,但它非常很难证明。没有人知道如何为每个窗口和每个消息保持他们自己的程序线程安全。显然你的不是,你没有尝试将消息发送与绘图联锁。你最好不要尝试,充其量你可以在后台缓冲区中进行渲染并翻页。通过适当的锁定,您可能可以让它发挥作用。 【参考方案1】:

引用来自微软网站上的PeekMessage

要检索其消息的窗口句柄。窗户 必须属于当前线程。

如果 hWnd 为 NULL,则 PeekMessage 检索任何窗口的消息 属于当前线程,以及当前线程上的任何消息 hwnd 值为 NULL 的线程的消息队列(参见 MSG 结构体)。因此,如果 hWnd 为 NULL,则窗口消息和线程 消息被处理。

如果 hWnd 为 -1,PeekMessage 只检索当前的消息 线程的hwnd值为NULL的消息队列,即thread PostMessage 发布的消息(当 hWnd 参数为 NULL 时)或 PostThreadMessage。

我怀疑线程没有“当前窗口”,并且“当前线程消息队列”不是 Windows 实际将消息作为默认线程发布到的那个。这只是一个假设,因为您的方法完全有可能存在其他问题(以及?)。但我相信这是问题所在的主要部分。

【讨论】:

感谢窗口提示。你是对的,它从我身边溜走了。但是,应用程序仍然挂起 :(。PeekMessage() 继续返回 0。 有什么东西正在向您正在查看的队列发送消息吗? 当窗口被激活或不被激活时,或者当它被破坏等时,应该发送/接收消息。这些由我的 WndProc() 处理。但是,应用程序启动后,窗口变得无响应。代码写得很简单,没有线程安全(只是尝试多线程),所以可能有很多错误。 窗口属于主线程(WinMain()),但存储在全局对象中。而且我还使用 AttachThreadInput() 将 2 个线程附加到主线程。 拥有第三个线程不是更好的主意,只需执行WaitForMultipleObject,然后在“释放”等待时向主线程发送消息。通过使用超时,您会在应用程序中引入延迟,这是不好的 - 如果超时时间足够短,它还会(不必要地)一直占用 CPU。

以上是关于GDI+ 多线程绘图的主要内容,如果未能解决你的问题,请参考以下文章

c#中利用system.timers多线程做图像处理,图像保存时提示“GDI+ 中发生一般性错误”,如何解决?

关于 Direct2D 绘图调用中的多线程

VC中怎么用多线程画图,不显示画图过程啊,我是新手

在 PictureBox 图像上使用线程绘图

文件句柄文件描述符与进程和多线程的那些事

android开发之多线程实现方法概述