需要把我的头脑围绕异步操作
本文关键字:异步操作 我的 | 更新日期: 2023-09-27 18:13:40
我有一个自定义控件,我有这个控件暴露给它的用户的接口。
public interface ILookupDataProvider
{
string IdColumnName { get; }
IEnumerable<IDataColumn> Metadata { get; set; }
void GetDataAsync(string parameters,
Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
}
所以,这是我的尝试暴露异步操作在GetDataAsync
但是我不知道如何在实现接口的类中实现这个方法。我理解这部分,因为我有一个方法,将执行,然后onCompletion
, onSucess
或onError
委托将被调用。
有人能帮我写这些语法吗?
编辑:这是4.0,我不能使用await
命令
我使用DevForce框架来加载数据,但是为了这个示例-让我们做WCF服务为例。如何在接口实现中包装WCF服务调用?
另外,你认为创建这样的接口来呈现异步操作是可以的吗?你会用不同的方式来处理事件吗?
这里的基本思想是查询将在后台线程中发生。一旦操作完成,您将使用onSuccess
和onError
回调来报告新值。例如
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);
}
你真的不想使用这个模式:
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
将允许您指示取消时采取的操作。
请注意,这是在异步CTP中使用await
和async
的方法当前被建模的方式;他们返回Task
或Task<T>
;在您的代码中做同样的事情将允许您在这些语言特性被嵌入时做好准备。
要使用任务,可以这样使用它。唯一要记住的棘手的事情是在你的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" };
});
}