WPF 多线程实现方式详解及应用示例

一、主要知识点说明及代码示例

(一)BackgroundWorker 组件
  • 知识点说明
    BackgroundWorker 是 WPF 中一种相对简单易用的多线程实现方式,它对线程相关的复杂操作进行了封装,使得开发者能较轻松地在后台执行任务并更新 UI 进度。通过设置相关属性(如 WorkerReportsProgress 表示是否可报告进度,WorkerSupportsCancellation 表示是否支持取消操作),关联对应的事件(DoWork 执行后台任务、ProgressChanged 用于在 UI 线程更新进度、RunWorkerCompleted 处理任务完成后的操作),就能实现基本的多线程任务处理且避免直接跨线程访问 UI 元素导致的异常。

  • 代码示例及注释

    using System.ComponentModel;
    using System.Windows;
    namespace WPFBackgroundWorkerExample
    {
    public partial class MainWindow : Window
    {
        private BackgroundWorker backgroundWorker;
        // 1. 在窗口构造函数中初始化 BackgroundWorker
        public MainWindow()
        {
            InitializeComponent();
            backgroundWorker = new BackgroundWorker();
            // 设置可以报告进度
            backgroundWorker.WorkerReportsProgress = true;
            // 设置支持异步取消操作(本示例未详细体现取消操作)
            backgroundWorker.WorkerSupportsCancellation = true;
            // 关联 DoWork 事件,在此事件中执行后台任务
            backgroundWorker.DoWork += BackgroundWorker_DoWork;
            // 关联 ProgressChanged 事件,用于在 UI 线程更新进度显示
            backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
            // 关联 RunWorkerCompleted 事件,处理任务完成后的操作
            backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 2. 点击按钮时,如果 BackgroundWorker 不忙就启动它执行后台任务
            if (!backgroundWorker.IsBusy)
            {
                backgroundWorker.RunWorkerAsync();
            }
        }
        // 3. 此方法在后台线程中执行具体任务,模拟耗时操作并报告进度
        private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 100; i++)
            {
                // 模拟耗时操作,这里简单地循环计数并暂停一段时间
                System.Threading.Thread.Sleep(100);//()换成英文()
                // 报告进度,会触发 ProgressChanged 事件
                backgroundWorker.ReportProgress(i);
            }
        }
        // 4. 在 UI 线程中更新进度条的值,根据 ProgressChanged 事件传递的进度百分比来更新
        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // 假设界面上有个名为 progressBar 的进度条控件,将其值设置为接收到的进度百分比
            progressBar.Value = e.ProgressPercentage;
        }
        // 5. 任务完成后触发此事件,可进行一些后续提示等操作,比如弹出提示框告知用户任务已完成
        private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("任务已完成");
        }
    }
    }
(二)Task 并行库
  • 知识点说明
    Task 并行库是功能强大且灵活的多线程实现方式,基于现代的异步编程模型。它能方便地实现各种复杂的多线程任务编排,像并行执行多个任务、等待多个任务完成以及任务的延续等操作。配合 asyncawait 关键字使用,可以让异步代码的编写更简洁易懂,增强代码的可读性。不过对于初学者来说,理解其深层次的任务状态管理、异常处理等概念可能需要花费一定时间。

  • 代码示例及注释

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            // 创建并启动一个异步任务,这里模拟一个耗时的计算任务
            Task<int> task = Task.Run(() =>
            {
                int result = 0;
                for (int i = 0; i < 1000000; i++)
                {
                    result += i;
                }
                return result;
            });
    
            // 等待任务完成,并且不会阻塞 UI 线程,使用 await 关键字可以让代码逻辑看起来是顺序执行的
            int finalResult = await task;
            // 在 UI 线程更新界面显示结果,这里假设界面上有个名为 resultTextBlock 的文本块控件
            resultTextBlock.Text = $"计算结果:{finalResult}";
        }
(三)Thread 类(直接使用线程)
  • 知识点说明
    直接使用 Thread 类是最基础的线程操作方式,它提供了对线程的高度控制,开发者可以灵活设置线程的优先级、状态等各种属性,适用于对线程有特殊要求的底层操作场景。但这种方式需要开发者自己处理很多复杂细节,比如线程同步以防止多个线程访问共享资源出现问题,以及手动切换到 UI 线程来更新界面元素(否则会出现异常),容易导致代码复杂且易出错。

  • 代码示例及注释

    using System;
    using System.Threading;
    using System.Windows;
    namespace WPFThreadExample
    {
    public partial class MainWindow : Window
    {
        private Thread thread;
    
        public MainWindow()
        {
            InitializeComponent();
            // 1. 创建一个 Thread 实例,并关联要执行的方法 DoWork
            thread = new Thread(DoWork);
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 2. 点击按钮时,如果线程处于未启动状态就启动它
            if (thread.ThreadState == ThreadState.Unstarted)
            {
                thread.Start();
            }
        }
        private void DoWork()
        {
            while (true)
            {
                // 3. 模拟从硬件设备读取数据等耗时操作,这里简单地休眠
                Thread.Sleep(1000);//()换成英文()
                // 4. 使用 Application.Current.Dispatcher.Invoke 方法切换到 UI 线程更新界面,假设界面有个名为 dataTextBlock 的文本块显示读取的数据
                Application.Current.Dispatcher.Invoke(() =>
                {
                    dataTextBlock.Text = $"新数据:{DateTime.Now}";
                });
            }
        }
    }
    }

    希望通过以上对各知识点的详细说明及示例代码展示,能帮助初学者更好地理解和掌握 WPF 多线程的不同实现方式及其应用场景。