univers1314 2025-12-31 11:07 采纳率: 28.6%
浏览 6

并发事件,是不是要多线程编写?

我在MFC里放了两个按钮,和一个EDIT控件框用来显示。
一个RUN按钮是执行我要的程序,这个程序执行时间比较长(比如是10-30才能完成分钟。)。另一个是STOP按钮是要停止当前执行的程序。类似于一些软件在保存时要停止它的效果。
我简单的写了一下,按下RUN按钮后,再按STOP按钮程序就傻在那里,要么就是没有响应,只能等待RUN执行完毕后才能反应过来。
这个逻辑是不是要多线程编写才行?

img


```c++
void CExDlg::OnBnClickedRun()
{
    // TODO: 在此添加控件通知处理程序代码
    CString rn = _T("\r\n");
    CString str;
    int i = 0;

    stop_flag = 1;
    while (stop_flag)
    {
        // 假设有一个CEdit控件的成员变量 m_edit1
        i++;
        str.Format(_T("%d"), i);    //int转成STring
        m_edit1.SetSel(-1);            //编辑框定位到末尾
        m_edit1.ReplaceSel(str);    //输出字符串

        m_edit1.SetSel(-1);            //编辑框定位到末尾
        m_edit1.ReplaceSel(rn);        //输出字符串
        Sleep(500);                    //延时500毫秒
        if (i == 20)                //计数到了10就清零 之后不再循环了。
        {
            stop_flag = 0;            //停止 置0
        }
    }
}


void CExDlg::OnBnClickedStop()
{
    // TODO: 在此添加控件通知处理程序代码
    bool stop_flag = 0;        //停止 置0
}


```

  • 写回答

3条回答 默认 最新

  • 檀越@新空间 2025-12-31 11:08
    关注

    上午好☀️☀️☀️️
    本答案参考通义千问

    是的,你遇到的问题 确实需要使用多线程来解决。在 MFC(Microsoft Foundation Classes)中,如果你在主线程(UI线程)中执行长时间的操作(如运行一个耗时10-30分钟的任务),那么该线程将被“阻塞”,导致界面无法响应任何操作,包括点击“STOP”按钮。


    ✅ 问题分析

    你当前的代码逻辑如下:

    • OnBnClickedRun() 中执行了一个长时间的循环,这会阻塞 UI 线程
    • OnBnClickedStop() 中只是简单地设置了一个变量 stop_flag,但因为主程序仍在运行,这个变量不会影响到正在运行的循环。

    因此,按下 STOP 按钮后,程序没有反应,因为它仍然在主线程中执行。


    🔧 解决方案:使用多线程

    要实现“运行”和“停止”的功能,必须将耗时任务放在子线程中执行,这样主线程才能继续响应用户输入。

    ✅ 具体步骤如下:

    1. 创建一个工作线程(Worker Thread) 来执行耗时任务。
    2. 使用共享变量或事件对象 来控制线程的执行与终止。
    3. 通过线程安全的方式更新 UI 控件(如 CEdit 控件)。

    📦 修改后的代码示例

    以下是一个完整的修改版本,包含线程管理、停止标志、以及线程安全的 UI 更新。

    1. 定义全局变量或类成员变量

    // 在你的对话框类中添加
    volatile bool stop_flag = false; // 停止标志,volatile 防止编译器优化
    HANDLE g_hThread = NULL;         // 线程句柄
    

    2. OnBnClickedRun() 函数(启动线程)

    void CExDlg::OnBnClickedRun()
    {
        if (g_hThread != NULL) {
            // 如果已有线程在运行,先关闭它
            TerminateThread(g_hThread, 0);
            CloseHandle(g_hThread);
            g_hThread = NULL;
        }
    
        stop_flag = false;
    
        // 启动线程
        g_hThread = CreateThread(
            NULL,                   // 默认安全属性
            0,                      // 默认堆栈大小
            RunThreadProc,          // 线程函数
            this,                   // 参数传递给线程函数
            0,                      // 默认创建标志
            NULL);                  // 不需要返回线程ID
    
        if (g_hThread == NULL) {
            AfxMessageBox(_T("线程创建失败!"));
        }
    }
    

    3. OnBnClickedStop() 函数(停止线程)

    void CExDlg::OnBnClickedStop()
    {
        stop_flag = true; // 设置停止标志
    }
    

    4. 线程函数 RunThreadProc

    UINT CExDlg::RunThreadProc(LPVOID pParam)
    {
        CExDlg* pDlg = (CExDlg*)pParam;
        CString rn = _T("\r\n");
        CString str;
        int i = 0;
    
        while (!pDlg->stop_flag)
        {
            i++;
            str.Format(_T("%d"), i);
    
            // 使用 PostMessage 或者调用 UI 函数时要注意线程安全
            pDlg->PostMessage(WM_UPDATE_EDIT, (WPARAM)str.GetBuffer(), (LPARAM)rn.GetBuffer());
    
            Sleep(500);
    
            if (i == 20)
            {
                break;
            }
        }
    
        return 0;
    }
    

    5. 添加自定义消息处理(WM_UPDATE_EDIT)

    在你的对话框类中添加以下内容:

    afx_msg LRESULT CExDlg::OnUpdateEdit(WPARAM wParam, LPARAM lParam);
    

    然后在 .cpp 文件中实现:

    LRESULT CExDlg::OnUpdateEdit(WPARAM wParam, LPARAM lParam)
    {
        CString str((LPCTSTR)wParam);
        CString rn((LPCTSTR)lParam);
    
        m_edit1.SetSel(-1);
        m_edit1.ReplaceSel(str);
        m_edit1.ReplaceSel(rn);
    
        return 0;
    }
    

    6. 在头文件中添加消息映射

    DECLARE_MESSAGE_MAP()
    ...
        ON_MESSAGE(WM_UPDATE_EDIT, OnUpdateEdit)
    END_MESSAGE_MAP()
    

    📝 注意事项

    • 不要在子线程中直接操作 UI 控件,否则可能导致崩溃或不可预测的行为。
    • 使用 PostMessage 是一种线程安全的方式,将更新请求发送回主线程。
    • 使用 volatile 修饰 stop_flag,防止编译器优化导致变量失效。
    • 为了更好的用户体验,可以考虑加入线程状态提示、错误处理等。

    ✅ 总结

    | 问题 | 解决方案 | |------|----------| | 主线程阻塞 | 使用多线程执行耗时任务 | | 停止按钮无效 | 使用共享变量控制线程执行 | | UI 不响应 | 将 UI 更新操作移到主线程 |


    如果你希望进一步优化,还可以考虑使用 CWinThreadstd::thread,但上述方式已经足够满足你的需求。

    评论

报告相同问题?

问题事件

  • 创建了问题 今天