在UI线程上下文中执行代码的正确方法?

时间:2015-01-12 07:50:35

标签: c# multithreading

我想从另一个帖子更新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:我不知道如何实施这样的事情

还有另外一种方法吗?哪个是首选方式?

注意:调用Task的线程不是同一个UI线程所以也许Progress不能在这里工作

2 个答案:

答案 0 :(得分:5)

你让它变得过于复杂,IMO:

F.Invoke((MethodInvoker)delegate { F.ls1.Items.Add("NewItem"); });

会很好地运作。你需要 async吗?我不认为你这样做,因为这不太可能是一项昂贵的操作:为此只释放工作线程并没有太多好处。

注意:如果F直接封装了更多内容,我个人更喜欢它,请注意;例如:

F.Invoke((MethodInvoker)delegate { F.AddCategory("NewItem"); });

答案 1 :(得分:2)

我总是建议开发人员避免Invoke / BeginInvoke / RunAsync,因为它会鼓励设计不佳:您的后端/业务逻辑类型最终会驱动用户界面(并采取依赖关系)在特定的UI框架上)。

更好的方法是使用IProgress<T> / Progress<T>(如果您的UI更新是进度更新)或IObservable<T>(如果您的后端异步生成一系列值)。< / p>

向列表中添加项目听起来更像是异步系列,但由于您特别询问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层将使用这些进度报告/值并相应地更新UI。