c#简单背景线程

时间:2012-11-27 09:58:28

标签: c# multithreading background task

我正在寻找一种在后台执行任务的简单方法,然后在完成时更新某些内容(在主线程上)。它处于低级“模型”类中,因此我无法调用InvokeOnMainThread,因为我没有NSObject。我有这个方法:

public void GetItemsAsync(Action<Item[]> handleResult)
{
  Item[] items=null;
  Task task=Task.Factory.StartNew(() =>
  {
    items=this.CreateItems(); // May take a second or two
  });
  task.ContinueWith(delegate
  {
    handleResult(items);
  }, TaskScheduler.FromCurrentSynchronizationContext());
}

这似乎工作正常,但

1)这是最好的(最简单的)方式吗?

2)我担心局部变量:

Item{} items=null

当方法在后台线程完成之前返回时,什么停止消失?

感谢。

4 个答案:

答案 0 :(得分:2)

这样的事情:

    public void GetItemsAsync(Action<Item[]> handleResult)
    {
        int Id = 11;
        Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two
        task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
    }

答案 1 :(得分:2)

我认为您的方法略有违反Single Responsibility Principle,因为它做得太多了。

首先,我建议更改CreateItems以返回Task,而不是将其包含在GetItemsAsync中:

public Task<Item[]> CreateItems(CancellationToken token)
{
   return Task.Factory.StartNew(() => 
     // obtaining the data...
     {});
}

CancellationToken是可选项,但如果您可以取消此长时间运行操作,则可以为您提供帮助。

使用此方法,您可以完全删除GetItemsAsync,因为客户端处理结果非常简单,而无需传递此委托:

// Somewhere in the client of your class
var task = yourClass.CreateItems(token);
task.ContinueWith(t => 
 // Code of the delegate that previously
 // passed to GetItemsAsync method
 {}, TaskScheduler.FromCurrentSynchronizationContext());

使用这种方法,您只需承担一项责任即可获得更清晰的代码。 Task类本身是将异步操作表示为first class object的完美工具。使用提出的技术,您可以轻松地模拟当前实现,并使用虚假行为进行单元测试,而无需更改客户端代码。

答案 2 :(得分:1)

您的代码看起来很好。

如果您可以使用C#5,这是何时使用async / await的完美示例,但如果没有,您必须像继续使用它一样编写它。

你的lambda中捕获了items变量,所以也没关系。

当您编写使用外部变量的lambda时,C#编译器会创建一个包含该变量的类。这称为闭包,意味着您可以访问lambda中的变量。

答案 3 :(得分:1)

这是一个更好的灵魂:

    public void GetItemsAsync(Action<Item[]> handleResult)
    {
        var task = Task.Factory.StartNew<Item[]>(() =>
        {
            return this.CreateItems(); // May take a second or two
        });

        task.ContinueWith(delegate
        {
            handleResult(task.Result);
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }