首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >InvokeRequired方法- codereview帮助

InvokeRequired方法- codereview帮助
EN

Stack Overflow用户
提问于 2010-07-15 06:54:16
回答 2查看 1.2K关注 0票数 2

首先,我不是Window.Forms开发经验丰富的人。但是,对于控件,我发现在线程应用程序中使用InvokeRequired检查有点繁琐。我已经创建了一个静态方法,我认为它解决了我繁琐的InvokeRequired检查。只是想把它公开,看看它是否是一种糟糕的“模式”:

代码语言:javascript
复制
public static void UIInvoke(Control uiControl, Action action)
{
    if (!uiControl.IsDisposed)
    {
        if (uiControl.InvokeRequired)
        {
            uiControl.BeginInvoke(action);
        }
        else
        {
            action();
        }
    }
}

好的,所以我有一个文本框(名为StatusTextBox),我想在后台线程中设置一些文本。这方面的守则是:

代码语言:javascript
复制
ThreadUtilities.UIInvoke(this.StatusTextBox, delegate()
{
    string text = this.StatusTextBox.Text;
    this.StatusTextBox.Text = (text.Length > 10) ? String.Empty : text.PadRight(1, '.');
});

这和这一样吗?

代码语言:javascript
复制
this.StatusTextBox.BeginInvoke(delegate()
{
    string text = this.StatusTextBox.Text;
    this.StatusTextBox.Text = (text.Length > 10) ? String.Empty : text.PadRight(1, '.');
});

谷歌( Google )在其最佳状态下,发现了 这篇文章 ,有人想出了同样的方法。我会继续我的“骚扰”。谢谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-07-15 10:43:53

我坚信,任何依赖于InvokeRequired的代码都遵循着糟糕的模式。

每个方法要么知道它在UI线程的上下文中运行,要么知道它不是。可以从任何线程运行的唯一方法应该是在同步类(如Semaphore )上运行(如果您正在编写同步类,请问问自己是否真的应该这样做)。

这是多线程设计的一个原则,它减少了bug并澄清了代码。

对于从后台线程更新UI的问题,我建议使用调度到UI的Task SynchronizationContext对象。如果Task类不可用(例如,针对4.0前的框架),则使用BackgroundWorker

下面是一个支持向UI报告进度以及支持取消和错误情况的后台任务的示例:

代码语言:javascript
复制
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

class Program
{
  [STAThread]
  static void Main()
  {
    // Set up the UI and run it.

    var program = new Program
    {
      startButton = new Button
      {
        Text = "Start",
        Height = 23, Width = 75,
        Left = 12, Top = 12,
      },
      cancelButton = new Button
      {
        Text = "Cancel",
        Enabled = false,
        Height = 23, Width = 75,
        Left = 93, Top = 12,
      },
      progressBar = new ProgressBar
      {
        Width = 156, Height = 23,
        Left = 12, Top = 41,
      },
    };

    var form = new Form
    {
      Controls =
        {
          program.startButton,
          program.cancelButton,
          program.progressBar
        },
    };

    program.startButton.Click += program.startButton_Click;
    program.cancelButton.Click += program.cancelButton_Click;
    Application.Run(form);
  }

  public Button startButton;
  public Button cancelButton;
  public ProgressBar progressBar;

  private CancellationTokenSource cancellationTokenSource;

  private void startButton_Click(object sender, EventArgs e)
  {
    this.startButton.Enabled = false;
    this.cancelButton.Enabled = true;

    this.cancellationTokenSource = new CancellationTokenSource();
    var cancellationToken = this.cancellationTokenSource.Token;
    var progressReporter = new ProgressReporter();
    var task = Task.Factory.StartNew(() =>
    {
      for (int i = 0; i != 100; ++i)
      {
        // Check for cancellation
        cancellationToken.ThrowIfCancellationRequested();

        Thread.Sleep(30); // Do some work.

        // Report progress of the work.
        progressReporter.ReportProgress(() =>
        {
          // Note: code passed to "ReportProgress" may access UI elements.
          this.progressBar.Value = i;
        });
      }

      // Uncomment the next line to play with error handling. 
      //throw new InvalidOperationException("Oops..."); 

      // The answer, at last! 
      return 42;
    }, cancellationToken);

    // ProgressReporter can be used to report successful completion,
    //  cancelation, or failure to the UI thread. 
    progressReporter.RegisterContinuation(task, () =>
    {
      // Update UI to reflect completion. 
      this.progressBar.Value = 100;

      // Display results. 
      if (task.Exception != null)
        MessageBox.Show("Background task error: " + task.Exception.ToString());
      else if (task.IsCanceled)
        MessageBox.Show("Background task cancelled");
      else
        MessageBox.Show("Background task result: " + task.Result);

      // Reset UI. 
      this.progressBar.Value = 0;
      this.startButton.Enabled = true;
      this.cancelButton.Enabled = false;
    });
  }

  private void cancelButton_Click(object sender, EventArgs e)
  {
    this.cancellationTokenSource.Cancel();
  }
}

这个示例代码使用了我为方便而定义的ProgressReporter (它清理了代码,IMO)。此类型为在我的博客上定义,如下所示:

代码语言:javascript
复制
/// <summary> 
/// A class used by Tasks to report progress or completion updates back to the UI. 
/// </summary> 
public sealed class ProgressReporter
{
  /// <summary> 
  /// The underlying scheduler for the UI's synchronization context. 
  /// </summary> 
  private readonly TaskScheduler scheduler;

  /// <summary> 
  /// Initializes a new instance of the <see cref="ProgressReporter"/> class. This should be run on a UI thread. 
  /// </summary> 
  public ProgressReporter()
  {
    this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
  }

  /// <summary> 
  /// Gets the task scheduler which executes tasks on the UI thread. 
  /// </summary> 
  public TaskScheduler Scheduler
  {
    get { return this.scheduler; }
  }

  /// <summary> 
  /// Reports the progress to the UI thread. This method should be called from the task. Note that the progress update is asynchronous with respect to the reporting Task. For a synchronous progress update, wait on the returned <see cref="Task"/>. 
  /// </summary> 
  /// <param name="action">The action to perform in the context of the UI thread. Note that this action is run asynchronously on the UI thread.</param> 
  /// <returns>The task queued to the UI thread.</returns> 
  public Task ReportProgressAsync(Action action)
  {
    return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, this.scheduler);
  }

  /// <summary> 
  /// Reports the progress to the UI thread, and waits for the UI thread to process the update before returning. This method should be called from the task. 
  /// </summary> 
  /// <param name="action">The action to perform in the context of the UI thread.</param> 
  public void ReportProgress(Action action)
  {
    this.ReportProgressAsync(action).Wait();
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task finishes execution, whether it finishes with success, failiure, or cancellation. 
  /// </summary> 
  /// <param name="task">The task to monitor for completion.</param> 
  /// <param name="action">The action to take when the task has completed, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle completion. This is normally ignored.</returns> 
  public Task RegisterContinuation(Task task, Action action)
  {
    return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.None, this.scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task finishes execution, whether it finishes with success, failiure, or cancellation. 
  /// </summary> 
  /// <typeparam name="TResult">The type of the task result.</typeparam> 
  /// <param name="task">The task to monitor for completion.</param> 
  /// <param name="action">The action to take when the task has completed, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle completion. This is normally ignored.</returns> 
  public Task RegisterContinuation<TResult>(Task<TResult> task, Action action)
  {
    return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.None, this.scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task successfully finishes execution. 
  /// </summary> 
  /// <param name="task">The task to monitor for successful completion.</param> 
  /// <param name="action">The action to take when the task has successfully completed, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle successful completion. This is normally ignored.</returns> 
  public Task RegisterSucceededHandler(Task task, Action action)
  {
    return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, this.scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task successfully finishes execution and returns a result. 
  /// </summary> 
  /// <typeparam name="TResult">The type of the task result.</typeparam> 
  /// <param name="task">The task to monitor for successful completion.</param> 
  /// <param name="action">The action to take when the task has successfully completed, in the context of the UI thread. The argument to the action is the return value of the task.</param> 
  /// <returns>The continuation created to handle successful completion. This is normally ignored.</returns> 
  public Task RegisterSucceededHandler<TResult>(Task<TResult> task, Action<TResult> action)
  {
    return task.ContinueWith(t => action(t.Result), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, this.Scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task becomes faulted. 
  /// </summary> 
  /// <param name="task">The task to monitor for faulting.</param> 
  /// <param name="action">The action to take when the task has faulted, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle faulting. This is normally ignored.</returns> 
  public Task RegisterFaultedHandler(Task task, Action<Exception> action)
  {
    return task.ContinueWith(t => action(t.Exception), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, this.Scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task becomes faulted. 
  /// </summary> 
  /// <typeparam name="TResult">The type of the task result.</typeparam> 
  /// <param name="task">The task to monitor for faulting.</param> 
  /// <param name="action">The action to take when the task has faulted, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle faulting. This is normally ignored.</returns> 
  public Task RegisterFaultedHandler<TResult>(Task<TResult> task, Action<Exception> action)
  {
    return task.ContinueWith(t => action(t.Exception), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, this.Scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task is cancelled. 
  /// </summary> 
  /// <param name="task">The task to monitor for cancellation.</param> 
  /// <param name="action">The action to take when the task is cancelled, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle cancellation. This is normally ignored.</returns> 
  public Task RegisterCancelledHandler(Task task, Action action)
  {
    return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
  }

  /// <summary> 
  /// Registers a UI thread handler for when the specified task is cancelled. 
  /// </summary> 
  /// <typeparam name="TResult">The type of the task result.</typeparam> 
  /// <param name="task">The task to monitor for cancellation.</param> 
  /// <param name="action">The action to take when the task is cancelled, in the context of the UI thread.</param> 
  /// <returns>The continuation created to handle cancellation. This is normally ignored.</returns> 
  public Task RegisterCancelledHandler<TResult>(Task<TResult> task, Action action)
  {
    return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
  }
}
票数 3
EN

Stack Overflow用户

发布于 2010-07-15 06:58:37

您的静态UIInvoke方法看起来不错,我看不出它有什么问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3253133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档