【问题标题】:C# Xamarin Forms - Executing task, with timeoutC# Xamarin Forms - 执行任务,超时
【发布时间】:2017-08-05 04:19:20
【问题描述】:

像许多其他人一样,我需要编写一个返回任务的函数,并且我希望该任务在一段时间后自动超时。

初始代码如下所示:

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 是最“轻量级”的(需要最少的系统资源)?

还有其他方法可以设置我错过的超时吗?

附言

我发现了一条很困难的知识——如果你从主 UI 线程之外的线程调用 GetDataFromServer():

Task.Run(() => await GetDataFromServer());    

在 iOS 上,第四种方法 (Xamarin.Forms.Device.StartTimer) 永远不会触发

【问题讨论】:

  • 第一个函数是异步的,所以它应该返回字符串,而不是任务。如果您希望此函数返回任务本身,请删除 async 关键字
  • 我添加了 'await' 功能,这就是我使用 'async' 关键字的原因。
  • 但这甚至无法编译...看起来您正在混合同步和异步代码。首先你向服务器发送一个异步请求,然后是一个带有回调的同步请求。带有public async Task&lt;string&gt;...... 的函数应该返回一个字符串
  • 你是对的,这不是我的实际代码,而只是帮助解释我的问题的示例代码。我已更新代码以删除“异步”。

标签: c# xamarin timeout async-await


【解决方案1】:

我认为只使用Task.DelayTask.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 - 不确定其中的原因。它本质上是DelayWhenAny 的低效版本。

方法#4:可以接受。尽管您确实必须处理 TaskCompletionSource&lt;T&gt; 及其怪癖(例如,默认情况下同步延续)。

【讨论】:

  • 所以现在我们有5种方法来实现超时!我仍然对每种方法的优缺点感到好奇,为什么在某些特定情况下您可能会选择其中一种...
  • @BettyCrokker:已编辑意见。
  • 为什么返回“await requestTask”而不是“requestTask”?
  • @BettyCrokker:那不会编译。
  • 对。抱歉,我对 async/await 比较陌生,仍然习惯于奇怪的语法。
猜你喜欢
  • 2021-05-28
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多