MFC 在线程中通过PostMessage更新界面主线程

本文介绍如何在MFC对话框中使用CThread类创建新线程,并通过自定义消息更新进度条控件。文章包含线程回调函数、消息映射及对话框响应处理等关键代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自 http://www.cnblogs.com/greatverve/archive/2012/12/21/mfc-thread-CProgressCtrl.html

几个要点:
1.对话框中启动新线程。把句柄做参数传递。
2.线程类中定义静态函数,也可以在对话框类中定义个全局函数。
3.线程回调函数必须是静态,且以void*为参数。
4.定义自定义消息,线程类发送,对话框类响应。




CThread类

// Thread.h
#pragma once
#include <process.h>
#include <Windows.h>

#define WM_USER_MSG WM_USER + 1001

class CThread
{
public:
    // 线程回调函数必须是全局函数
    static void Run(void *ptr);
public:
    CThread(void);
    ~CThread(void);
};
// Thread.cpp
#include "StdAfx.h"
#include "Thread.h"

void CThread::Run( void *ptr )
{
    HWND hWnd = (HWND)ptr;
    for (int i=0;i<100;++i)
    {
        ::PostMessage(hWnd,WM_USER_MSG,WPARAM(i),LPARAM(0));
        Sleep(100);
    }
    AfxMessageBox("完成");
    ::PostMessage(hWnd,WM_USER_MSG,WPARAM(0),LPARAM(0));
    _endthread();
}

CThread::CThread(void)
{
}
CThread::~CThread(void)
{
}


对话框类

// 消息映射
ON_MESSAGE(WM_USER_MSG, &CThreadProgDlg::OnMsg)
// OnInitDialog
CProgressCtrl *prog = (CProgressCtrl *)GetDlgItem(IDC_PROGRESS1);
prog->SetRange(0,100);

void CThreadProgDlg::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    _beginthread(&CThread::Run,0,this->GetSafeHwnd());
    //CDialogEx::OnOK();
}

LRESULT CThreadProgDlg::OnMsg( WPARAM wp,LPARAM lp )
{
    CProgressCtrl *prog = (CProgressCtrl *)GetDlgItem(IDC_PROGRESS1);
    prog->SetPos((int)wp);
    return 1L;
}

附件源码: http://download.youkuaiyun.com/detail/liubaoxyz/9634059

密码:blog.youkuaiyun.com/liubaoxyz

<think>嗯,用户问的是MFC中子线主线程是否存在内存可见性问题。这个问题看起来是关于多线中的内存同步和线安全的内容。首先,我需要回忆一下MFC(Microsoft Foundation Classes)的基本结构,以及它在多线处理方面的机制。 MFC作为微软的一个旧框架,主要用于Windows应用序开发。在MFC中,线分为用户界面线主线程,通常处理窗口消息)和工作线程(子线,执行后台任务)。用户的问题核心是这两个线之间的内存可见性,也就是一个线对内存的修改是否能被另一个线立即看到。 接下来,我需要考虑多线中的内存模型。在C++中,如果没有适当的同步机制,不同线对同一内存区域的访问可能导致数据竞争,进而引发未定义行为。MFC虽然提供了一些线同步的类,如CCriticalSection、CMutex、CEvent等,但内存可见性问题依然存在,因为编译器或处理器可能会进行指令重排,导致线间看到的数据不一致。 然后,我需要具体说明MFC中的情况。例如,子线修改了一个全局变量,主线程是否能立即看到这个变化。如果没有使用互斥锁或内存屏障,可能会出现缓存不一致的问题,导致主线程读取到旧值。这里需要提到volatile关键字的作用,但也要指出volatile在C++中并不能保证原子性,只能防止编译器优化,而MFC可能在某些旧代码中使用volatile,但在现代C++中应使用原子变量或同步机制。 另外,需要讨论MFC的消息传递机制。例如,使用PostMessage或SendMessage在主线程中处理消息,这种方式是否能安全地传递数据。通过消息队列传递数据可以避免直接的内存共享,从而减少可见性问题,但如果是传递指针或引用,仍需要注意同步。 还要考虑MFC中的窗口对象线关联性,比如窗口句柄只能在创建它的线中访问,否则可能导致问题。这可能涉及到跨线操作时的安全措施,如使用Invoke或BeginInvoke方法(虽然在MFC中可能需要自己封装类似的机制)。 然后,需要给出解决方案。建议使用同步对象,比如临界区来保护共享数据,或者使用原子操作。此外,推荐使用消息传递而非共享内存的方式来减少竞争条件的发生。同时,指出在MFC中处理线间通信的最佳实践,比如通过自定义消息传递数据,而不是直接访问共享变量。 最后,总结一下,MFC中的子线主线程确实存在内存可见性问题,需要通过同步机制来确保数据的一致性。同时,考虑到MFC的历史背景,可能还存在一些旧有的编习惯需要注意更新,比如使用现代C++的特性来增强线安全性。</think>在 MFC(Microsoft Foundation Classes)中,子线主线程之间**确实存在内存可见性问题**,这本质上是由于多线中普遍存在的内存同步问题导致的。以下是关键点分析: --- ### **1. 内存可见性问题的根源** - **CPU 缓存与指令重排**:现代处理器会对内存访问进行优化(如缓存、乱序执行),可能导致线间的数据修改无法立即可见。 - **缺乏同步机制**:如果未正确使用同步原语(如锁、原子操作),线可能读取到过期的内存状态。 - **MFC 框架特性**:MFC 本身未对线间内存可见性提供隐式保证,需开发者显式处理。 --- ### **2. MFC 中的线模型** - **主线程UI 线)**:负责处理窗口消息(如按钮点击、绘图),直接操作 UI 控件。 - **子线工作线程)**:用于执行耗时任务(如计算、I/O),**禁止直接操作 UI 控件**(需通过消息传递间接操作)。 --- ### **3. 共享数据的风险示例** ```cpp // 全局变量(共享内存) int g_counter = 0; // 子线修改数据 UINT WorkerThread(LPVOID pParam) { for (int i = 0; i < 1000; i++) { g_counter++; // 非原子操作,存在竞争条件 } return 0; } // 主线程读取数据 void OnButtonClick() { AfxBeginThread(WorkerThread, nullptr); // 若直接读取 g_counter,可能获得中间状态或不一致的值 CString str; str.Format(L"Counter: %d", g_counter); MessageBox(str); } ``` **问题**:`g_counter` 的修改可能因缓存未刷新或指令重排,导致主线程读取到旧值。 --- ### **4. 解决方案** #### **(1) 使用同步对象** - **临界区(`CCriticalSection`)**: ```cpp CCriticalSection g_cs; UINT WorkerThread(LPVOID pParam) { g_cs.Lock(); g_counter++; g_cs.Unlock(); return 0; } ``` - **互斥量(`CMutex`)**:适用于跨进同步。 - **事件(`CEvent`)**:通过信号机制协调线。 #### **(2) 原子操作** - 使用 Windows API 的原子函数(如 `InterlockedIncrement`): ```cpp #include <windows.h> long g_counter = 0; UINT WorkerThread(LPVOID pParam) { InterlockedIncrement(&g_counter); return 0; } ``` #### **(3) 消息传递(UI 更新)** - 通过 `PostMessage` 或 `SendMessage` 通知主线程更新 UI,避免直接共享 UI 对象: ```cpp // 子线发送自定义消息 PostMessage(hWnd, WM_USER_UPDATE_UI, (WPARAM)new_value, 0); // 主线程处理消息 BEGIN_MESSAGE_MAP(CMyDlg, CDialog) ON_MESSAGE(WM_USER_UPDATE_UI, OnUpdateUI) END_MESSAGE_MAP() LRESULT CMyDlg::OnUpdateUI(WPARAM wParam, LPARAM lParam) { m_editCtrl.SetWindowTextW((LPCWSTR)wParam); return 0; } ``` --- ### **5. 特殊注意事项** - **UI 对象线亲缘性**:MFC 的窗口对象(如 `CWnd`)必须由创建它的线(通常是主线程)操作,否则会引发断言错误。 - **`volatile` 的局限性**:`volatile` 仅阻止编译器优化,不保证原子性或内存屏障,需结合同步机制使用。 - **内存屏障**:在极低层代码中,可能需要 `MemoryBarrier()` 强制刷新内存状态。 --- ### **总结** MFC 中子线主线程的内存可见性问题需通过显式同步(锁、原子操作)或消息传递解决。直接共享内存是高风险行为,而通过消息机制间接通信(如更新 UI)是 MFC 推荐的安全做法。务必遵循“UI 操作仅在主线程执行”的原则,避免跨线资源竞争。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值