InvokeRequired方法 - codereview帮助

时间:2010-07-15 06:54:16

标签: c# multithreading

开始使用我没有经验的Window.Forms开发。但是,我发现对于控件的InvokeRequired检查在线程应用程序中使用时有点单调乏味。我创建了一个静态方法,我认为这解决了我繁琐的InvokeRequired检查。只是想把它扔到空位,看看它是否是一个糟糕的“模式”:

public static void UIInvoke(Control uiControl, Action action)
{
    if (!uiControl.IsDisposed)
    {
        if (uiControl.InvokeRequired)
        {
            uiControl.BeginInvoke(action);
        }
        else
        {
            action();
        }
    }
}

好的,所以我有一个文本框(名为StatusTextBox),我想在后台线程中设置一些文本。代码将是:

ThreadUtilities.UIInvoke(this.StatusTextBox, delegate()
{
    string text = this.StatusTextBox.Text;
    this.StatusTextBox.Text = (text.Length > 10) ? String.Empty : text.PadRight(1, '.');
});

这是否与?相同?

this.StatusTextBox.BeginInvoke(delegate()
{
    string text = this.StatusTextBox.Text;
    this.StatusTextBox.Text = (text.Length > 10) ? String.Empty : text.PadRight(1, '.');
});

谷歌处于最佳状态,发现this article有人想出了同样的方法。我会在那里继续我的“骚扰”。谢谢!

2 个答案:

答案 0 :(得分:3)

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

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

这是多线程设计的原则,可以减少错误并澄清代码。

对于从后台线程更新UI的问题,我建议使用安排在用户界面Task的{​​{1}}对象。如果SynchronizationContext类不可用(例如,定位4.0之前的框架),请使用Task

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

BackgroundWorker

此示例代码使用我为方便起见而定义的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(); } } (它清理代码,IMO)。此类型为defined on my blog

ProgressReporter

答案 1 :(得分:0)

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