You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程应用中如何用Callback替代Control.Invoke实现RichTextBox输出?

多线程文件处理改用Callback实现UI更新

原代码通过在工作线程中直接调用Control.Invoke/BeginInvoke更新RichTextBox,现在需要改用**Callback(回调)**的方式实现,核心是让业务逻辑类(FilesHandler)与UI控件解耦,通过委托回调通知UI层进行更新。

步骤1:定义回调委托

首先定义一个委托类型,用于传递文件处理过程中的更新信息:

// 定义回调委托,用于传递UI更新文本
public delegate void FileProcessingUpdateCallback(string updateText);

步骤2:修改FilesHandler类,移除UI控件依赖

重构FilesHandler,不再直接持有RichTextBox,而是通过回调委托通知UI更新:

class FilesHandler
{
    private List<string> PathesOfFiles = new List<string>();

    public void LoadPathes(string pathOfDirectory)
    {
        string[] allfiles = Directory.GetFiles(pathOfDirectory);
        foreach (string filename in allfiles)
        {
            PathesOfFiles.Add(filename);
        }
    }

    // 改为接收回调委托,而非直接传入RichTextBox
    public void HandleEachFile(FileProcessingUpdateCallback updateCallback)
    {
        foreach (string path in PathesOfFiles)
        {
            // 启动线程时传入回调
            new Thread(() => CalculateThread(updateCallback, path)).Start();
        }
    }

    private void CalculateThread(FileProcessingUpdateCallback updateCallback, string path)
    {
        // 生成时间戳文本,通过回调传递给UI层
        string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + "\t";
        updateCallback(timestamp);

        List<string> resultLog = new List<string>();
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        resultLog.Add(GetFileName(path) + " {" + string.Join(", ", CountBytes(path)) + " } ");
        stopwatch.Stop();
        resultLog.Add(stopwatch.ElapsedMilliseconds.ToString() + " ms\n");

        // 传递处理结果给UI层
        updateCallback(string.Join("", resultLog));
    }

    private string GetFileName(string path)
    {
        return System.IO.Path.GetFileName(path);
    }

    private ulong[] CountBytes(string filename)
    {
        ulong[] Bytes = new ulong[256];

        byte[] buffer = new byte[4096 * 1000];
        int bytesRead = 0;
        using (FileStream reader = File.OpenRead(filename))
            while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0)
                for (int i = 0; i < bytesRead; Bytes[buffer[i++]]++) ;

        return Bytes;
    }
}

步骤3:UI层实现回调方法并调用

在WinForm窗体中,实现回调方法处理线程安全的UI更新,并调用FilesHandler:

public partial class MainForm : Form
{
    private FilesHandler _fileHandler = new FilesHandler();

    public MainForm()
    {
        InitializeComponent();
    }

    // 触发文件处理的示例按钮点击事件
    private void btnProcessFiles_Click(object sender, EventArgs e)
    {
        string targetDir = @"C:\YourTargetDirectory"; // 替换为实际目录
        _fileHandler.LoadPathes(targetDir);
        // 传入回调方法
        _fileHandler.HandleEachFile(UpdateRichTextBoxCallback);
    }

    // 回调方法,负责线程安全地更新RichTextBox
    private void UpdateRichTextBoxCallback(string textToAdd)
    {
        // 判断是否需要跨线程调用
        if (richTextBox1.InvokeRequired)
        {
            richTextBox1.Invoke(new Action<string>(UpdateRichTextBoxCallback), textToAdd);
        }
        else
        {
            richTextBox1.Text += textToAdd;
        }
    }
}

核心说明

  • 解耦逻辑:FilesHandler现在只负责文件处理业务,不再依赖UI控件,符合单一职责原则。
  • 回调作用:通过委托将更新信息传递给UI层,UI层自己处理线程安全的控件更新,业务逻辑无需关心UI细节。
  • 线程安全:回调方法内部依然保留InvokeRequired判断,确保UI操作在主线程执行。

内容的提问来源于stack exchange,提问作者StepanVyacheslavovich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 02:46:22