C#中实现跨线程写入的示例代码

在C#中实现跨线程写入UI控件(如WinForms、WPF)需要使用特定的方法,因为UI控件通常只能在创建它们的线程(通常是主线程)上访问。以下是几种常见的实现方式:

图片[1]_C#中实现跨线程写入的示例代码_知途无界

1. WinForms中的跨线程写入

方法1:使用 Control.Invoke(推荐)​

using System;
using System.Threading;
using System.Windows.Forms;

public partial class Form1 : Form
{
    private Label labelStatus;
    private Button buttonStart;

    public Form1()
    {
        InitializeComponent();
        // 初始化控件
        labelStatus = new Label();
        buttonStart = new Button();
        buttonStart.Text = "启动线程";
        buttonStart.Click += ButtonStart_Click;
        this.Controls.Add(labelStatus);
        this.Controls.Add(buttonStart);
    }

    private void ButtonStart_Click(object sender, EventArgs e)
    {
        // 启动新线程执行耗时操作
        Thread thread = new Thread(new ThreadStart(LongRunningOperation));
        thread.IsBackground = true;
        thread.Start();
    }

    private void LongRunningOperation()
    {
        // 模拟耗时操作
        for (int i = 0; i <= 100; i++)
        {
            // 跨线程更新UI
            UpdateStatus($"处理进度: {i}%");
            
            // 模拟工作
            Thread.Sleep(50);
        }
        
        UpdateStatus("操作完成!");
    }

    // 跨线程安全更新Label文本
    private void UpdateStatus(string message)
    {
        // 检查是否需要Invoke
        if (labelStatus.InvokeRequired)
        {
            // 使用Invoke在UI线程上执行
            labelStatus.Invoke(new Action<string>(UpdateStatus), message);
        }
        else
        {
            // 当前就是UI线程,直接更新
            labelStatus.Text = message;
        }
    }

    private void InitializeComponent()
    {
        this.SuspendLayout();
        this.ClientSize = new System.Drawing.Size(400, 200);
        this.Text = "跨线程写入示例";
        this.ResumeLayout(false);
    }
}

方法2:使用 Control.BeginInvoke(异步调用)​

private void UpdateStatusAsync(string message)
{
    if (labelStatus.InvokeRequired)
    {
        // 使用BeginInvoke异步执行,不阻塞工作线程
        labelStatus.BeginInvoke(new Action<string>(UpdateStatusAsync), message);
    }
    else
    {
        labelStatus.Text = message;
    }
}

方法3:使用 SynchronizationContext

private SynchronizationContext syncContext;

public Form1()
{
    InitializeComponent();
    // 获取UI线程的同步上下文
    syncContext = SynchronizationContext.Current;
}

private void UpdateStatusWithSyncContext(string message)
{
    syncContext.Post(_ => 
    {
        labelStatus.Text = message;
    }, null);
}

2. WPF中的跨线程写入

方法1:使用 Dispatcher.Invoke

using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;

public partial class MainWindow : Window
{
    private TextBlock textStatus;
    private Button buttonStart;

    public MainWindow()
    {
        InitializeComponent();
        
        // 创建UI元素
        StackPanel panel = new StackPanel();
        
        textStatus = new TextBlock();
        textStatus.Margin = new Thickness(10);
        
        buttonStart = new Button();
        buttonStart.Content = "启动线程";
        buttonStart.Margin = new Thickness(10);
        buttonStart.Click += ButtonStart_Click;
        
        panel.Children.Add(textStatus);
        panel.Children.Add(buttonStart);
        
        this.Content = panel;
    }

    private void ButtonStart_Click(object sender, RoutedEventArgs e)
    {
        Thread thread = new Thread(new ThreadStart(LongRunningOperation));
        thread.IsBackground = true;
        thread.Start();
    }

    private void LongRunningOperation()
    {
        for (int i = 0; i <= 100; i++)
        {
            UpdateStatus($"处理进度: {i}%");
            Thread.Sleep(50);
        }
        
        UpdateStatus("操作完成!");
    }

    private void UpdateStatus(string message)
    {
        // WPF中使用Dispatcher
        if (textStatus.Dispatcher.CheckAccess())
        {
            // 当前就是UI线程
            textStatus.Text = message;
        }
        else
        {
            // 使用Dispatcher在UI线程上执行
            textStatus.Dispatcher.Invoke(() =>
            {
                textStatus.Text = message;
            });
        }
    }

    private void InitializeComponent()
    {
        // WPF初始化代码
    }
}

方法2:使用 Dispatcher.BeginInvoke

private void UpdateStatusAsync(string message)
{
    textStatus.Dispatcher.BeginInvoke(new Action(() =>
    {
        textStatus.Text = message;
    }));
}

3. 通用工具类封装

为了简化跨线程操作,可以创建一个通用的扩展方法:

WinForms 扩展方法

public static class ControlExtensions
{
    public static void InvokeIfRequired(this Control control, Action action)
    {
        if (control.InvokeRequired)
        {
            control.Invoke(action);
        }
        else
        {
            action();
        }
    }

    public static void InvokeIfRequired<T>(this Control control, Action<T> action, T parameter)
    {
        if (control.InvokeRequired)
        {
            control.Invoke(action, parameter);
        }
        else
        {
            action(parameter);
        }
    }
}

// 使用示例
private void UpdateStatusWithExtension(string message)
{
    labelStatus.InvokeIfRequired(() => 
    {
        labelStatus.Text = message;
    });
}

WPF 扩展方法

public static class UIElementExtensions
{
    public static void DispatchIfRequired(this DispatcherObject uiElement, Action action)
    {
        if (uiElement.Dispatcher.CheckAccess())
        {
            action();
        }
        else
        {
            uiElement.Dispatcher.Invoke(action);
        }
    }
}

// 使用示例
private void UpdateStatusWithExtension(string message)
{
    textStatus.DispatchIfRequired(() => 
    {
        textStatus.Text = message;
    });
}

4. 使用 async/await 的现代方式(推荐)​

WinForms 中使用 async/await

private async void ButtonStart_Click(object sender, EventArgs e)
{
    try
    {
        buttonStart.Enabled = false;
        await LongRunningOperationAsync();
    }
    catch (Exception ex)
    {
        MessageBox.Show($"错误: {ex.Message}");
    }
    finally
    {
        buttonStart.Enabled = true;
    }
}

private async Task LongRunningOperationAsync()
{
    await Task.Run(() =>
    {
        for (int i = 0; i <= 100; i++)
        {
            // 使用TaskScheduler.FromCurrentSynchronizationContext确保在UI线程更新
            var updateAction = new Action(() => 
            {
                labelStatus.Text = $"处理进度: {i}%";
            });
            
            // 切换到UI线程执行
            Task.Factory.StartNew(updateAction, CancellationToken.None, 
                TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
            
            Thread.Sleep(50);
        }
    });
    
    // 自动回到UI线程
    labelStatus.Text = "操作完成!";
}

WPF 中使用 async/await

private async void ButtonStart_Click(object sender, RoutedEventArgs e)
{
    buttonStart.IsEnabled = false;
    try
    {
        await LongRunningOperationAsync();
    }
    finally
    {
        buttonStart.IsEnabled = true;
    }
}

private async Task LongRunningOperationAsync()
{
    await Task.Run(() =>
    {
        for (int i = 0; i <= 100; i++)
        {
            Application.Current.Dispatcher.Invoke(() =>
            {
                textStatus.Text = $"处理进度: {i}%";
            });
            
            Thread.Sleep(50);
        }
    });
    
    textStatus.Text = "操作完成!";
}

5. 重要注意事项

  1. 不要阻塞UI线程​:长时间运行的操作一定要放在后台线程
  2. 及时释放资源​:使用 Thread.IsBackground = true 确保线程不会阻止程序退出
  3. 异常处理​:跨线程操作中要妥善处理异常
  4. 性能考虑​:频繁的小更新可以使用 BeginInvoke 避免阻塞工作线程
  5. 死锁预防​:避免在UI线程等待后台线程完成时使用 Invoke

这些方法可以根据具体需求选择使用,async/await 是现代C#推荐的异步编程模式,代码更清晰易读。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞16 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容