关于new等函数对任务管理器中内存、句柄数变化的影响

本文通过在XP系统和VS2008 DEBUG模式下测试,探讨new操作如何影响任务管理器显示的内存和句柄数变化。详细分析了内存分配和对象创建对系统资源的实际占用情况。

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

以下代码在XP+VS2008 DEBUG环境下测试:

 

#include <stdio.h>
#include <windows.h>

typedef void (*PFNDo)();

void runtest(PFNDo pfn) { while(1) { pfn();Sleep(100); } }

void do_nop() { ; }
void do_new() { int* p = new int[5]; }
void do_event() { HANDLE hEvent = CreateEvent(NULL,FALSE,TRUE,NULL); }
void do_mapping() { HANDLE hFile = CreateFileMapping(NULL,NULL,0,0,1024,"CHINA20130327CCTVTEST"); }
void do_file() { HANDLE hFile = CreateFile("D:\\testfile",GENERIC_READ,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); }
void do_thread() { ; }
void do_process() { HANDLE hProcess = OpenProcess(0,FALSE,3092); }

int main()
{
	//
	// (内存使用,虚拟内存大小,句柄数,线程数)
	//
	unsigned int op = 1;
	switch(op)
	{
	case 0:runtest(do_nop);break;	// 一直维持在(652K,224K,7,1)
	case 1:runtest(do_new);break;	// 内存和虚拟内存持续增加,某个时刻结果为(700K,276K,7,1)
	case 2:runtest(do_event);break;	// 句柄数持续增加,某个时刻结果为(656K,224K,604,1)
	case 3:runtest(do_mapping);break;	// 一直维持在(656K,224K,7,1)
	case 4:runtest(do_file);break;	// 一直维持在(656K,224K,8,1)
	case 5:runtest(do_process);break;	// 一直维持在(660K,224K,7,1)
	default:break;
	}
	return 0;
}

<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 操作仅在主线程执行”的原则,避免跨线程资源竞争。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值