像其他许多人一样,我需要编写一个返回任务的函数,我希望该任务在一定时间段内自动超时。
初始代码看起来像这样:
class MyClass
{
TaskCompletionSource<string> m_source;
public Task<string> GetDataFromServer()
{
m_source = new TaskCompletionSource<string> ();
// System call I have no visibility into, and that doesn't inherently take any
// sort of timeout or cancellation token
ask_server_for_data_and_when_youve_got_it_call(Callback);
return m_source.Task;
}
protected void Callback(string data);
{
// Got the data!
m_source.TrySetResult(data);
}
}
现在,我希望这会变得更聪明,并且在适当的时候就可以了。我有几个选择:
class MyClass
{
TaskCompletionSource<string> m_source;
public Task<string> GetDataFromServer(int timeoutInSeconds)
{
m_source = new TaskCompletionSource<string> ();
ask_server_for_data_and_when_youve_got_it_call(Callback);
// Method #1 to set up the timeout:
CancellationToken ct = new CancellationToken ();
CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource (ct);
cts.CancelAfter (timeoutInSeconds * 1000);
cts.Token.Register(() => m_source.TrySetCancelled());
// Method #2 to set up the timeout:
CancellationTokenSource ct2 = new CancellationTokenSource ();
ct2.CancelAfter (timeoutInSeconds * 1000);
ct2.Token.Register (() => m_source.TrySetCancelled());
// Method #3 to set up the timeout:
System.Threading.Tasks.Task.Factory.StartNew (async () =>
{
await System.Threading.Tasks.Task.Delay (timeoutInSeconds * 1000);
m_source.TrySetCancelled();
});
// Method #4 to set up the timeout:
Xamarin.Forms.Device.StartTimer (new TimeSpan (0, 0, timeoutInSeconds),
() => m_source.TrySetCancelled());
return m_source.Task;
}
protected void Callback(string data);
{
// Got the data!
m_source.TrySetResult(data);
}
}
设置超时的4种不同方式的杂物和流失是什么?例如,我猜该方法#2是最"轻量级"(需要最少的系统资源)?
还有其他方法可以设置我错过的超时吗?
P.S。
我发现的一条知识很难 - 如果您从主UI线程以外的线程调用getDatafromserver():
Task.Run(() => await GetDataFromServer());
在iOS上,第四种方法(xamarin.forms.device.starttimer)从不发射
我认为只使用Task.Delay
和Task.WhenAny
:
public async Task<string> GetDataFromServerAsync(int timeoutInSeconds)
{
Task<string> requestTask = GetDataFromServerAsync();
var timeoutTask = Task.Delay(timeoutInSeconds);
var completedTask = await Task.WhenAny(requestTask, timeoutTask);
if (completedTask == timeoutTask)
throw new OperationCanceledException();
return await requestTask;
}
其他方法的缺点:
方法#1:无缘无故地创建一个新的CancellationToken
。这只是方法#2的效率较低。
方法#2:通常,任务完成后,您应该处置Register
的结果。在这种情况下,由于CTS最终总是被取消。
方法#3:使用StartNew
只是调用Delay
-不确定那里的推理。它本质上是具有WhenAny
的Delay
的效率较低。
方法#4:可以接受。尽管您确实必须处理TaskCompletionSource<T>
及其怪癖(例如,默认情况下同步连续性)。