需要理解异步操作

4

我有一个自定义控件,它向用户公开了接口。

public interface ILookupDataProvider
{
    string IdColumnName { get; }

    IEnumerable<IDataColumn> Metadata { get; set; }

    void GetDataAsync(string parameters, 
        Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
}

所以,我的尝试是在GetDataAsync中公开异步操作。

但我不知道如何在实现接口的类中实现这个方法。我理解这部分的意思是,我有一个方法将被执行,然后调用onCompletiononSuccessonError委托。

有没有人可以帮忙写出这些语法呢?

编辑:

这是4.0版本,我不能使用await命令

编辑2:

我使用DevForce框架来加载数据,但为了这个示例 - 让我们以WCF服务为例。我怎样在我的接口实现中包装WCF服务调用呢?

另外,您认为创建这样的接口来表示异步操作是否可以?您会用事件等不同的方式来处理吗?


你是想了解async/await的用法,还是有其他方面需要了解? - flq
你打算使用.NET 3.5还是.NET 4? - Alois Kraus
我无法使用async await,因为我们正在使用VS2010进行生产。我想编写此接口的实现,但无法弄清语法。即使是它的简单非异步实现也应该帮助我理解如何针对Action编写代码。 - katit
这将取决于 GetDataAsync 做什么。它是从磁盘读取数据吗?还是进行 WCF 调用?或者是查询数据库?无论它调用的是什么,都可能有一个异步 API,您可以根据该 API 实现 GetDataAsync。为了帮助您,我们需要知道您将使用哪个 API。 - Joe White
@Joe,我编辑了主贴以提供更多关于我的工作的细节。 - katit
3个回答

6
基本思路是查询会在后台线程上进行。一旦您的操作完成,您将使用onSuccessonError回调来报告新值。例如:
void GetDataAsync(
  string parameters, 
  Action<IEnumerable<object>> onSuccess, 
  Action<Exception> onError) {

  WaitCallback doWork = delegate { 
    try { 
      IEnumerable<object> enumerable = GetTheData(parameters);
      onSuccess(enumerable);
    } catch (Exception ex) {
      onError(ex);
    }
  };

  ThreadPool.QueueUserWorkItem(doWork, null);
}

1
不知道为什么没有使用TaskTask<T>,这些类的整个目的是将异步操作完成的通知控制推到方法逻辑之外。 - casperOne

3

确实不建议使用这种模式:

void GetDataAsync(string parameters, 
    Action<IEnumerable<object>> onSuccess, Action<Exception> onError);

相反,你应该使用这个:
Task GetDataAsync(string parameters);

当返回一个Task时,你返回的是一个代表异步工作单位的实例。从那里,API的使用者可以选择调用ContinueWith并决定在成功或出现错误时该怎么做。
但是,在您的示例中存在设计缺陷。在名为GetDataAsync的方法中,我期望返回数据。这不是问题,您只需更改签名为:
Task<MyData> GetDataAsync(string parameters);

现在这将返回一个Task<T>,您可以使用Result属性来获取结果(如果任务未完成,则会阻塞),或者当异步操作完成时再次使用ContinueWith方法处理数据。

此外,您可以将CancellationToken结构实例作为参数,以确定是否应取消操作:

Task<MyData> GetDataAsync(string parameters, 
    CancellationToken cancellationToken);

ContinueWith 方法可以让您指定在取消操作时要执行的操作。

请注意,这是目前在Async CTP中使用awaitasync方法的方式;它们返回TaskTask<T>。在您的代码中使用相同的方式将使您准备好在这些语言特性被完全集成后立即使用。


我觉得你是对的,但是你的例子更加复杂,让我有些难以理解 :) 我知道我必须要学会,但还是有点困难。除非你能向我展示如何在实现接口的类中应用它,例如将 WCF 调用包装到其中。我不关心取消和继续操作,我只需要知道它何时完成,无论是出现异常还是返回数据。 - katit
好的,这个在Silverlight中可用吗?我认为Task不可用。 - katit

1

要使用任务,您可以像这样使用它。唯一需要记住的棘手之处是在您的UI线程中执行回调函数,这可以通过TaskScheduler.FromCurrentSynchronizationContext()来实现,以便您可以更新您的UI或在出现问题时显示消息框。

由于这些东西的事件驱动性质,如果您频繁点击启动WCF调用的按钮,可能会导致结果返回的顺序与您发送请求的顺序不一致。您可以通过存储已启动的任务并取消最后一个已启动的任务(如果您想启动新操作),或者在任务运行时简单地忽略后续的请求来防止这种情况发生。

private void button1_Click(object sender, EventArgs e)
{
    GetDataAsync("www.data.com").ContinueWith(result =>
        {
            if (result.Exception != null)
            {
                MessageBox.Show(this, "Error: {0}" + result.Exception, "Error");
            }
            else
            {
                foreach (var obj in result.Result)
                {
                    textBox1.Text += obj.ToString();
                }
            }
        },
        TaskScheduler.FromCurrentSynchronizationContext()
        );

}

Task<IEnumerable<object>> GetDataAsync(string parameters)
{
    return Task<IEnumerable<object>>.Factory.StartNew(() =>
    {
        Thread.Sleep(500);
      //  throw new ArgumentException("uups");
        // make wcf call here
        return new object[] { "First", "second" };
    });
}

很遗憾,我无法在Silverlight 4中使用任务。 - katit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接