gpt4 book ai didi

c# - InvokeRequired 方法 - codereview 帮助

转载 作者:行者123 更新时间:2023-11-30 22:44:58 27 4
gpt4 key购买 nike

首先,我在 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有人想出了同样的方法。我会在那里继续我的“骚扰”。谢谢!

最佳答案

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

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

这是多线程设计的一个原则,减少bug,让代码更清晰。

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

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

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)。这个类型是defined on my blog作为:

/// <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);
}
}

关于c# - InvokeRequired 方法 - codereview 帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3253133/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com