我有兴趣在解决方案中找到差距来处理在silverlight中链接的异步调用。如果您在解决方案中发现差距/问题,请与我们联系。或者,如果有其他方式/模式来解决问题,请指出正确的方向。
代码未编译或测试
问题
我们要求在完成一堆异步调用后执行一些操作。
例如,一旦服务调用完成,就将默认值分配给UI控件。
void Fun()
{
Servicecall1();
Servicecall2();
//Make sure the following is executed only after the previous async calls are finished.
AssignDefaultValues();
}
解决方案
执行此操作的一种方法是通过回调链接服务调用。但是,我只是想知道通过如下特殊异步执行跟踪类的对象来实现它有多么糟糕/好。
class SomeClass
{
AsyncMehodCallWatcher _methodcallwatcher = new AsyncMehodCallWatcher();
void fun()
{
_methodcallwatcher.AsyncMethodStartMultipleCall(() =>
{
//this function will be called automatically after the following service calls
AssignDefaultValues();
}, 2);
Servicecall1();
Servicecall2();
}
void Servicecall1()
{
serviceclient.DoSomething(DoSomethingcallback);
}
void DoSomethingcallback(object sender, GetCompletedEventArgs arg)
{
_methodcallwatcher.AsyncCallCompleted();
}
void Servicecall2()
{
serviceclient.DoSomething(DoSomething2callback);
}
void DoSomething2callback(object sender, GetCompletedEventArgs arg)
{
_methodcallwatcher.AsyncCallCompleted();
}
}
任何需要调用Servicecall1()或Servicecall2()的人都应该调用AsyncMethodPreCall(null)。
以下是AsyncMehodCallWatcher的建议实现
public class AsyncMehodCallWatcher
{
private int _counter = 0;
public AsyncMehodCallWatcher()
{
}
public void AsyncMethodPreCall()
{
AsyncMethodPreCall(null);
}
Action _callcompletedaction = null;
public void AsyncMethodStartMultipleCall(Action action, int numofcalls)
{
if (action != null)
{
if (_callcompletedaction != null)
{
throw new InvalidOperationException(LocalString.AsyncMehodCallWatcherErrorAsyncCalls);
}
_callcompletedaction = action;
_counter = numofcalls;
}
}
public void AsyncMethodPreCall(Action action)
{
if (action != null)
{
if (_callcompletedaction != null)
{
throw new InvalidOperationException(LocalString.AsyncMehodCallWatcherErrorAsyncCalls);
}
_callcompletedaction = action;
}
_counter++;
}
public void AsyncCallCompleted()
{
_counter--;
if (_counter < 0)
{
throw new InvalidOperationException(LocalString.AsyncMehodCallWatcherErrorAsyncCalls);
}
if (_counter == 0)
{
if (_callcompletedaction != null)
{
_callcompletedaction();
_callcompletedaction = null;
}
}
}
}
答案 0 :(得分:1)
如果使用任务并行库,则可以使用TaskCompletionSource将基于回调的方法转换为基于任务的方法。这是一个例子。
public class SomeClass
{
public async Task Fun()
{
// execute both service requests at the same time
Task<Result> fooTask = ServiceCallAsync1();
Task<Result> barTask = ServiceCallAsync2();
// wait for both of them to be complete.
Result[] t = await Task.WhenAll(new[] { fooTask, barTask });
AssignDefaultValues();
}
public Task<Result> ServiceCallAsync1()
{
TaskCompletionSource<Result> completion = new TaskCompletionSource<Result>();
serviceclient.DoSomething(() =>
{
completion.SetResult(new FooResult());
});
return completion.Task;
}
public Task<Result> ServiceCallAsync2()
{
TaskCompletionSource<Result> completion = new TaskCompletionSource<Result>();
serviceclient.DoSomething(() =>
{
completion.SetResult(new BarResult());
});
return completion.Task;
}
private void AssignDefaultValues()
{
}
}
答案 1 :(得分:1)
以下是我在问题评论中的意思:
Task<GetCompletedEventArgs> CallServiceAsync(
Action<Action<object, GetCompletedEventArgs> callDoSomething)
{
var tcs = new TaskCompletionSource<GetCompletedEventArgs>();
callDoSomething((sender, arg) => tcs.SetResult(arg));
return tcs.Task;
}
async Task AsyncMethodStartMultipleCall(ServiceClient client)
{
var task1 = CallServiceAsync((callback) => client.DoSomething(callback));
var task2 = CallServiceAsync((callback) => client.DoSomething(callback));
var task3 = CallServiceAsync((callback) => client.DoSomething(callback));
await Task.WhenAll(task1, task2, task3);
}
我强烈建议将Microsoft.Bcl.Async
用于Silverlight 4和.NET 4.0。如果你使用VS2012 +,它会给你async/await
。如果您因任何原因无法使用,请使用ContinueWhenAll
或者如果完成顺序很重要,请使用Stephen Toub撰写的Then
pattern作文。例如:
Task AsyncMethodStartMultipleCall(ServiceClient client)
{
var task1 = CallServiceAsync((callback) => client.DoSomething(callback));
var task2 = CallServiceAsync((callback) => client.DoSomething(callback));
var task3 = CallServiceAsync((callback) => client.DoSomething(callback));
return TaskFactory.ContinueWhenAll(new [] { task1, task2, task3 }, (tasks) =>
{
Debug.Print("All completed!");
});
}
如果你不打算use async/await
,请确保从UI线程中保存TaskScheduler.FromCurrentSynchronizationContext()
,并将其传递给ContinueWhenAll
或ContinueWith
,否则继续使用lambda可能会在池线程上调用。