在 UI 线程上下文中执行代码的正确方法?
the proper way to execute code in the UI Thread context?
我想从另一个线程UI更新
我有很多选择可以在 UI 线程上下文中执行代码:
1: 使用 BeginInvoke/EndInvoke 方法:
伪代码
public delegate int AddItem(object Item);
public Form1 F = (Form1)Application.OpenForms["Form1"];
private static async DoSomething()
{
AddItem ad = new AddItem(F.ls1.Items.Add);
await Task.Run(() =>F.EndInvoke(F.BeginInvoke(add,"NewItem")));
}
2: 使用 Progress / IProgress : 我不知道如何实现这样的东西
还有其他方法吗?哪个是首选方式?
注意: 调用任务的线程不一样UI线程所以Progress可能在这里不起作用
你把它弄得太复杂了,IMO:
F.Invoke((MethodInvoker)delegate { F.ls1.Items.Add("NewItem"); });
会很好用。你需要这里的async
吗?我不认为你会这样做,因为这不太可能是一项昂贵的操作:为此释放工作线程并没有太大好处。
注意:我个人更喜欢 F
直接封装更多内容,请注意;例如:
F.Invoke((MethodInvoker)delegate { F.AddCategory("NewItem"); });
我总是建议开发人员避免 Invoke
/BeginInvoke
/RunAsync
,因为它会助长糟糕的设计:您的 backend/business 逻辑类型最终会驱动 UI(并依赖于特定的 UI 框架)。
更好的方法是使用 IProgress<T>
/Progress<T>
(如果您的 UI 更新是进度更新)或 IObservable<T>
(如果您的后端异步生成一系列值)。
向列表添加项目对我来说更像是一个异步系列,但由于您特别询问了 IProgress<T>
我将展示一个例子:
private static async Task DoSomethingAsync()
{
Progress<object> progress = new Progress<object>(item => F.ls1.Items.Add(item));
await Task.Run(() => BackgroundWork(progress));
}
private static void BackgroundWork(IProgress<object> progress)
{
// Do background work here.
var item = "NewItem";
// Notify UI of progress.
if (progress != null)
progress.Report(item);
}
如果您想使用 IObservable<T>
,那么您的代码可能如下所示:
private static void StartSomething()
{
IObservable<object> items = BackgroundWork();
items.ObserveOn(F).Subscribe(item => F.ls1.Items.Add(item), ex => HandleException(ex));
}
请注意,对于这两种方法,执行后台工作的代码并不关心它在什么上下文中执行。它只生成一系列进度更新或一系列值。 UI 层然后消耗这些进度 reports/values 并相应地更新 UI。
我想从另一个线程UI更新
我有很多选择可以在 UI 线程上下文中执行代码:
1: 使用 BeginInvoke/EndInvoke 方法:
伪代码
public delegate int AddItem(object Item);
public Form1 F = (Form1)Application.OpenForms["Form1"];
private static async DoSomething()
{
AddItem ad = new AddItem(F.ls1.Items.Add);
await Task.Run(() =>F.EndInvoke(F.BeginInvoke(add,"NewItem")));
}
2: 使用 Progress / IProgress : 我不知道如何实现这样的东西
还有其他方法吗?哪个是首选方式?
注意: 调用任务的线程不一样UI线程所以Progress可能在这里不起作用
你把它弄得太复杂了,IMO:
F.Invoke((MethodInvoker)delegate { F.ls1.Items.Add("NewItem"); });
会很好用。你需要这里的async
吗?我不认为你会这样做,因为这不太可能是一项昂贵的操作:为此释放工作线程并没有太大好处。
注意:我个人更喜欢 F
直接封装更多内容,请注意;例如:
F.Invoke((MethodInvoker)delegate { F.AddCategory("NewItem"); });
我总是建议开发人员避免 Invoke
/BeginInvoke
/RunAsync
,因为它会助长糟糕的设计:您的 backend/business 逻辑类型最终会驱动 UI(并依赖于特定的 UI 框架)。
更好的方法是使用 IProgress<T>
/Progress<T>
(如果您的 UI 更新是进度更新)或 IObservable<T>
(如果您的后端异步生成一系列值)。
向列表添加项目对我来说更像是一个异步系列,但由于您特别询问了 IProgress<T>
我将展示一个例子:
private static async Task DoSomethingAsync()
{
Progress<object> progress = new Progress<object>(item => F.ls1.Items.Add(item));
await Task.Run(() => BackgroundWork(progress));
}
private static void BackgroundWork(IProgress<object> progress)
{
// Do background work here.
var item = "NewItem";
// Notify UI of progress.
if (progress != null)
progress.Report(item);
}
如果您想使用 IObservable<T>
,那么您的代码可能如下所示:
private static void StartSomething()
{
IObservable<object> items = BackgroundWork();
items.ObserveOn(F).Subscribe(item => F.ls1.Items.Add(item), ex => HandleException(ex));
}
请注意,对于这两种方法,执行后台工作的代码并不关心它在什么上下文中执行。它只生成一系列进度更新或一系列值。 UI 层然后消耗这些进度 reports/values 并相应地更新 UI。