C# Xamarin Forms - Выполнение задачи с тайм-аутом

Как и многим другим, мне нужно написать функцию, которая возвращает задачу, и я хочу, чтобы эта задача автоматически отключалась по истечении определенного периода времени.

Исходный код выглядит так:

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 является наиболее "легковесным" (требующим наименьшего количества системных ресурсов)?

Есть ли другие способы установить тайм-аут, который я пропустил?

п.с.

Один из знаний, который я нашел, был трудным путем - если вы вызываете GetDataFromServer() из потока помимо основного потока пользовательского интерфейса:

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

На iOS четвертый метод (Xamarin.Forms.Device.StartTimer) никогда не срабатывает

1 ответ

Я думаю, что проще использовать 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 - не уверен в рассуждениях там. Это по сути менее эффективная версия Delay с WhenAny,

Метод № 4: будет приемлемым. Хотя вы должны иметь дело с TaskCompletionSource<T> и его причуды (например, синхронные продолжения по умолчанию).

Другие вопросы по тегам