多线程应用中如何用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
相关产品推荐
相关产品推荐

