MFC应用程序,工作线程学习记录

如何在一个MFC应用程序中添加控制台输出(printf、cout)?

目标:在MFC应用程序中也可以使用cout,printf进行Debug

cpp 复制代码
在程序初始化时(InitInstance中,位于"工程名.cpp"中)调用
AllocConsole(); // 分配控制台
freopen("CONOUT$", "w", stdout); // 将stdout重定向到控制台


参数意义:
第一个参数 CONOUT$ : Windows 系统中的 "控制台输出设备"(Console Output)
是一个特殊的设备名(而非普通文件路径);
第二个参数 w :文件打开模式(Write,写入模式,还有其他模式)
第三个参数 stdout : 要重定向的标准流(Standard Output,标准输出)

freopen函数意义:
核心作用是重新关联标准流(如 stdin/stdout/stderr)与指定的文件或设备
简单说就是 "重定向输入 / 输出的目标"
示例:freopen("log.txt", "w", stdout);(后续 printf 内容会写入 log.txt)。

从属于C 标准库函数(头文件 <cstdio> 或 <stdio.h>)

这样操作以后,在启动MFC程序之后,会额外启动一个控制台窗口用来查看输出




如何简单实现多线程?(AfxBeginThread函数实现)

目标:使用多线程完成简单任务,并输出到控制台上

首先我们需要一个用于多线程执行的任务函数

cpp 复制代码
UINT MyWorkerThread(LPVOID pParam) 
{
    for (int i = 0; i < 100; i+=25) {
        printf("工作线程运行中%d%%\r\n", i);
        Sleep(1000);
    }
    printf("工作线程执行完毕!\n");
    return 0;
}

这里需要注意:

用 MFC 的 AfxBeginThread 或 Windows API 的 CreateThread必须符合 UINT/DWORD 返回值 + LPVOID 参数的原型。该函数的"格式"是十分严格 的,必须写成 "返回值为UINT、参数为LPVOID" 这样的格式

但是,如果:使用 C++ 标准库 std::thread:无强制原型限制,函数可以灵活定义。

或者:直接同步 调用(非线程):无任何限制,但也不是多线程


其次我们要设定启动该线程的方式方法,比如说启动应用程序即刻启动,还是点击某个按键后触发(这里我使用的是点击一个按钮触发该线程)

如何添加按钮这里不多赘述:

cpp 复制代码
void CTest24ThreadDlg::OnButtonStart()
{
	printf("线程即将启动\r\n");

	AfxBeginThread(MyWorkerThread,NULL);    //创建并启动多线程

	printf("线程结束了吗?\r\n");
}

最后观察运行结果,并进行测试。执行完,结果应该是:

cpp 复制代码
线程即将启动
线程结束了吗?
工作线程运行中:0%
工作线程运行中:25%
工作线程运行中:50%
工作线程运行中:75%
工作线程执行完毕!



工作线程与主线程(UI线程)通信及其交互

目标:通过工作线程,消息模式,来使得主线程完成反馈,而非单纯控制台反馈。

模式简述:

发送命令,准备工作:主线程(UI线程)通过发送指令令工作线程工作(或创建工作线程等)

受到命令,开始工作:工作线程完成自己的工作(UI线程仍可独立运行,不会阻塞)

完成工作,进行反馈:工作线程反馈完工信息等,反馈给主线程,令其有相应的反馈(弹窗等)

这就是一个消息模式流程。后续将围绕这几步骤进行实践


规定消息:无论是主界面还是工作界面都需要约定好的消息进行通信

这里采用宏定义进行定义消息:

cpp 复制代码
#define MESSAGE_A 8888

注意:宏定义的数值不可随便定义,因为有些数值已经被系统占用了
WM_USER + 100 是推荐的起始值,确保唯一性(据说)

处理函数:

在主线程窗口类中,声明并实现消息处理函数,用于接收工作线程的消息并更新 UI

cpp 复制代码
放在"工程名Dlg.h"位置,即主线程窗口类(c工程名Dlg 类)中Protected处

afx_msg LRESULT UpdateUI(WPARAM wParam, LPARAM lParam);


代码命名要求较为严苛:除函数名外几乎无法修改
afx_msg 关键字必须添加在函数声明前,用于告诉 MFC 消息映射机制:"这是一个消息处理函数"
返回值类型 LRESULT固定为 LRESULT(32 位整数类型),用于表示消息处理的结果
(大多数情况下返回 0 即可)
WPARAM wParam, LPARAM lParam,必须包含这两个参数:
WPARAM:传递短整数或指针(32/64 位系统中长度与平台一致)。
LPARAM:传递长整数或指针
(功能与 WPARAM 类似,历史上为区分长度设计。现在两者在 32/64 位系统中通常等价)。
这两个参数是 Windows 消息机制的标准设计,用于传递消息的附加数据(如进度值、指针等)。

在源文件(工程名Dlg.cpp)中绑定消息与处理函数(消息映射)

cpp 复制代码
在源文件(工程名Dlg.cpp)中绑定消息与处理函数(消息映射)

ON_MESSAGE(MESSAGE_A, &CMessageTestDlg::UpdateUI) 

ON_MESSAGE(消息ID, 消息处理函数指针)


要求放在
BEGIN_MESSAGE_MAP(C工程名Dlg, CDialog)  /(即你的主窗口类,其父类)
	//{{AFX_MSG_MAP(CMessageTestDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, MessageSend)
	//}}AFX_MSG_MAP

	ON_MESSAGE(MESSAGE_A, &C工程名Dlg::UpdateUI)

END_MESSAGE_MAP()
这里,也就是夹在begin和end中间
注意Begin后面的参数,要是自己主窗口那个类,其他的是其他窗口的
这串代码是一个消息映射宏,每一个界面都会有,只不过是有没有内容,内容一样不一样的区别

主线程消息处理函数:

cpp 复制代码
位于工程名Dlg.cpp中,信息映射后面

LRESULT CMessageTestDlg::UpdateUI(WPARAM wParam, LPARAM lParam) 
{
    MessageBox(_T("工作线程任务已完成!"), _T("提示"), MB_OK);
    return 0;
}

注意,定义同样严苛:LRESULT 类名::函数名(WPARAM wParam, LPARAM lParam);

发送消息:仍设计为点击按钮发送消息

未完待续

相关推荐
小坏讲微服务5 小时前
MaxWell中基本使用原理 完整使用 (第一章)
大数据·数据库·hadoop·sqoop·1024程序员节·maxwell
晚风吹长发5 小时前
二分查找算法+题目详解
c++·算法·二分查找
罗义凯6 小时前
其中包含了三种排序算法的注释版本(冒泡排序、选择排序、插入排序),但当前只实现了数组的输入和输出功能。
数据结构·c++·算法
春蕾夏荷_7282977257 小时前
c++ easylogging 使用示例
c++·log·easylogging
syt_biancheng7 小时前
Day3算法训练(简写单词,dd爱框框,3-除2!)
开发语言·c++·算法·贪心算法
自然数e8 小时前
C++多线程【线程管控】之线程转移以及线程数量和ID
开发语言·c++·算法·多线程
Elias不吃糖10 小时前
epoll 事件全集、每个事件的含义、哪些事件在实际服务器中最常见、哪些会组合出现
linux·c++·event
AA陈超10 小时前
ASC学习笔记0017:返回此能力系统组件的所有属性列表
c++·笔记·学习·ue5·虚幻引擎
Unlyrical11 小时前
splice, io_uring_prep_splice 调用(无效参数)
linux·服务器·c++·unix
Lucis__12 小时前
STL设计模式探秘:容器适配器&仿函数
c++·容器·stl·仿函数