【问题标题】:How to manipulate the two different controls on the UI thread at once?如何同时操作 UI 线程上的两个不同控件?
【发布时间】:2012-09-18 19:43:20
【问题描述】:

如何在保持应用程序的 UI 线程响应的同时持续操作标签的文本?我正在 Visual Studio 2010 中创建一个 Windows 窗体应用程序。此外,如果您可以在 vb.net 或 c# 中发布您的示例,我将非常感激。

【问题讨论】:

  • WPF 还是表单?这就是为什么我需要输入更多的问题?
  • 回答你的问题 Blam its Forms
  • 然后标记它以便正确分类。
  • 你不能在一个线程上同时做两件事。您可以在多个线程上一次在 UI 外部执行多项操作,但您必须对 UI 的更改进行排队,以便一次发生一项。您可以通过调用BeginInvoke (msdn.microsoft.com/en-us/library/…) 调用更改UI 线程上的UI 的代码来执行此操作。 msdn.microsoft.com/en-us/library/ms171728.aspx 提供了一个很好的概述。
  • 您的权利,彼得如果您愿意发表您的评论作为答案,我很乐意为您服务。

标签: .net winforms multithreading


【解决方案1】:

您必须从另一个线程执行此操作,使用 BeginInvoke,这将改变 UI 线程上 UI 控件的状态。

【讨论】:

  • 从非 UI 线程操作 UI?你试过吗?你会得到一个例外。
【解决方案2】:

如果您使用的是 .NET Framework 4,则可以使用异步任务。

这段代码将在一个新线程上启动一些东西:

var myBackgroundTask = Task.Factory.StartNew(() => MyBackGroundMethod());

你说你要更新用户界面(UI),好吧,因为UI上的控件是在主线程上创建的,直接从另一个线程访问它们会抛出跨线程异常。但是,您可以从后台线程启动一个新任务并告诉它在主线程上运行,如下所示:

private void MyMethod()
{
   var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
   var myBackgroundTask = Task.Factory.StartNew(() => MyBackGroundMethod(uiTaskScheduler));
}

private void MyBackGroundMethod(TaskScheduler uiTaskScheduler)
{
    Task.Factory.StartNew(
            () => { TextBox1.Text = "Hello from separate thread"; },
            CancellationToken.None,
            TaskCreationOptions.None,
            uiTaskScheduler);
}

如果你想在任务完成后做某事,你可以像这样使用延续:

var finishedTask = myBackgroundTask.ContinueWith(
                    x => Label1.Text = "Task 1 completed sucessfully",
                    CancellationToken.None,
                    TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
                    uiTaskScheduler);

注意 TaskContinuationOptions - 您可以指定仅在没有错误时继续,或者仅在取消、未取消等(更多)时继续。

如果您想取消长时间运行的进程,您可以使用 CancellationToken。

这是一个使用后台任务、更新进度、允许取消以及在完成、错误、取消或完成时报告的完整示例。

这是一个带有 2 个按钮、1 个标签和 1 个进度条的表单。分别命名为 StartButton、StopButton、ProgressBar、InformationLabel。

public partial class MainForm : Form
{
    CancellationTokenSource cancelTokenSource;

    public MainForm()
    {
        InitializeComponent();
    }

    private void StartButton_Click(object sender, EventArgs e)
    {
        InformationLabel.Text = "Task 1 running...";
        var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        cancelTokenSource = new CancellationTokenSource();

        this.StopButton.Enabled = true;
        this.StartButton.Enabled = false;

        var someWork = Task.Factory.StartNew(
            () => { DoWork(uiTaskScheduler); },
            cancelTokenSource.Token,
            TaskCreationOptions.None,
            TaskScheduler.Default);

        var someWorkFinished = someWork.ContinueWith(
                x => InformationLabel.Text = "Task 1 completed sucessfully",
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
                uiTaskScheduler);

        var someWorkErrored = someWork.ContinueWith(
                x => InformationLabel.Text = "Task 1 Errored",
                CancellationToken.None,
                TaskContinuationOptions.OnlyOnFaulted,
                uiTaskScheduler);

        var someWorkCancelled = someWork.ContinueWith(
                x => InformationLabel.Text = "Task 1 Cancelled",
                CancellationToken.None,
                TaskContinuationOptions.OnlyOnCanceled,
                uiTaskScheduler);

        var resultTasks = new List<Task>();
        resultTasks.Add(someWorkFinished);
        resultTasks.Add(someWorkErrored);
        resultTasks.Add(someWorkCancelled);

        Task.Factory.ContinueWhenAny(
            resultTasks.ToArray(),
            x => this.Reset(),
            CancellationToken.None,
            TaskContinuationOptions.None,
            uiTaskScheduler);
    }

    private void Reset()
    {
        this.StopButton.Enabled = false;
        this.StartButton.Enabled = true;
    }

    private void DoWork(TaskScheduler uiTaskScheduler)
    {
        for (int i = 0; i < 10; i++)
        {
            cancelTokenSource.Token.ThrowIfCancellationRequested();

            Thread.Sleep(500);
            i++;

            // Update progress
            Task.Factory.StartNew(
                () => { UpdateProgress(ProgressBar, i * 10); }, 
                CancellationToken.None, 
                TaskCreationOptions.None, 
                uiTaskScheduler);
        }
    }

    private void UpdateProgress(ProgressBar progressBar, int i)
    {
        progressBar.Value = i;
    }

    private void StopButton_Click(object sender, EventArgs e)
    {
        cancelTokenSource.Cancel();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    相关资源
    最近更新 更多