Предотвращение зависания пользовательского интерфейса при использовании Task.Result - PullRequest
0 голосов
/ 16 ноября 2018

Я вызываю Task.Run (() => DoSomething ()). Результат, который приводит к зависанию пользовательского интерфейса и происходит потому, что я использую «.Result».Мне нужен Result, потому что я хочу вернуть значение.

Я не хочу, чтобы метод StartSomething был асинхронным, потому что я не хочу ждать метод StartSomething.Я хочу, чтобы ожидание происходило в DoSomething ().

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

Можно ли улучшить этот код или есть другое решение?

private TaskCompletionSource<bool> TaskCompletion = null;
private void Button_Click(object sender, RoutedEventArgs e)
    {
        bool k = StartSomething();
    }

    private bool StartSomething()
    {
        return Task.Run(() => DoSomething()).Result;
    }

    private async Task<bool> DoSomething()
    {
        TaskCompletion = new TaskCompletionSource<bool>();
        await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
        MessageBox.Show("DoSomething");
        return true;
    }

1 Ответ

0 голосов
/ 16 ноября 2018

Метод StartSomething() не имеет смысла для меня. Он запускает новый Task, а затем просто синхронно ожидает результат (.Result) этой задачи, который практически бесполезен - он почти [*] такой же, как звонить DoSomething() напрямую. Также DoSomething() уже асинхронный, поэтому вам не нужно начинать новый Task для него.

Похоже, вам не нужен StartSomething() метод вообще. Если вы сделаете Button_Click обработчик async, вы можете просто await DoSomething() напрямую:

private TaskCompletionSource<bool> TaskCompletion = null;

private async void Button_Click(object sender, RoutedEventArgs e)
{
    bool k = await DoSomething();
}

private async Task<bool> DoSomething()
{
    TaskCompletion = new TaskCompletionSource<bool>();
    await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
    MessageBox.Show("DoSomething");
    return true;
}

1020 *
*

Edit:

При использовании асинхронного решения до конца (как показано выше) IMO является предпочтительным способом, если вы действительно не можете изменить код вызова на async, я могу придумать два способа вызова async метод из синхронного метода без блокировки пользовательского интерфейса. Во-первых, вручную настроить задачи продолжения, например:

private void Button_Click(object sender, RoutedEventArgs e)
{
    DoSomething().ContinueWith((task) =>
        {
            bool k = task.Result;

            // use the result
        },

        // TaskScheduler argument is needed only if the continuation task
        // must run on the UI thread (eg. because it access UI elements).
        // Otherwise this argument can be omitted.
        TaskScheduler.FromCurrentSynchronizationContext());

    // Method can exit before DoSomething().Result becomes
    // available, which keep UI responsive
}

Таким образом, вы в основном разделяете синхронный метод (одно разбиение вместо каждого await) на несколько частей (лямбда-методы продолжения), связанных .ContinueWith. Это похоже на то, что await делает под капотом. Проблема в том, что в отличие от await (который производит хороший и чистый код), ваш код будет заполнен этими лямбдами продолжения. И будет намного хуже, когда вы добавите блоки обработки исключений, using блоки и т. Д.

Второй подход - использование вложенных циклов, например. Метод расширения Стивена Туба * WaitWithNestedMessageLoop :

static T WaitWithNestedMessageLoop<T>(this Task<T> task)
{
    var nested = new DispatcherFrame();
    task.ContinueWith(_ => nested.Continue = false, TaskScheduler.Default);
    Dispatcher.PushFrame(nested);
    return task.Result;
}

Вложенные циклы - довольно продвинутая техника (я на самом деле никогда не использовал ее), и я не рекомендую использовать ее, если не нужно.

<Ч />

[*] Существуют различия в обработке исключений, выполнении потоков и т. Д., Но они не имеют отношения к этому вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...