Вопрос по async/await для UI
От: Somescout  
Дата: 19.11.19 14:40
Оценка:
Тема не по самому UI, а скорее по его архитектуре, поэтому создал в .NET, но если нужно перенесите в .NET GUI

Имеет ли право на жизнь такое использование async/await:

Модальный диалог:
class ModalDialog<TItem> {

  private TaskCompletionSource<TItem> taskSource = new TaskCompletionSource<TItem>();

  public Task<Result> ExecuteModal() {
    this.Show();
    return taskSource.Task;
  }

  private void OkButtonClicked() {
    taskSource.SetResult(state);
  } 
}


Его вызов:
class SomeView {

  private async void GetUserSelection() {
      var dialog = new ModalDialog { ... };
      var userSelection = await dialog.ExecuteModal();

      // Doing something with result
  }
}


То есть правильно ли ожидать закрытия диалога через async/await? Вроде по тестам дедлока не происходит. И если чуть усложнить вопрос: может ли произойти дедлок, если вызов taskSource.SetResult был сделан через синхронизацию с потоком UI из другого потока:
  private async void OnMessageArrived() {  // Вызывается не из потока ui
    InvokeAsync(() => { // Передаём выполнение в поток UI
      taskCompletionSource.SetResult(someResult); // сигнализируем о завершении диалога
    });
  }


И, кстати, обязательно ли в таком случае переводить выполнение в поток UI, или же Task сам выберет правильный поток, из какого-бы потока не вызывали SetResult?
ARI ARI ARI... Arrivederci!
Re: Вопрос по async/await для UI
От: BlackEric http://black-eric.lj.ru
Дата: 19.11.19 19:18
Оценка:
Здравствуйте, Somescout, Вы писали:

S>И, кстати, обязательно ли в таком случае переводить выполнение в поток UI, или же Task сам выберет правильный поток, из какого-бы потока не вызывали SetResult?


Да, обязательно. Иначе taska будет выполняться в случайном потоке.
https://github.com/BlackEric001
Re: Вопрос по async/await для UI
От: RushDevion Россия  
Дата: 20.11.19 06:49
Оценка:
S>Имеет ли право на жизнь такое использование async/await:

Имхо, работать-то будет, но целесообразность как-то сомнительна.
Диалоговое окно — это по определению что-то блокирующее основной UI, какой здесь смысл в асинхронщине?
Если только у тебе этих окошек открывается по несколько штук одновременно.

S>То есть правильно ли ожидать закрытия диалога через async/await? Вроде по тестам дедлока не происходит...

Дедлока не будет, но возможен unhandled exception, который не перехватится через try/catch (async void).
Если бы сигнатура была такой async Task GetUserSelection, то дедлок был бы возможен, в зависимости от того, как вызывается метод.


S>И, кстати, обязательно ли в таком случае переводить выполнение в поток UI, или же Task сам выберет правильный поток, из какого-бы потока не вызывали SetResult?

Имхо, не обязательно (если, конечно GetUserSelection, из ui потока изначально вызывается).
private async void GetUserSelection() {
      var dialog = new ModalDialog { ... };
      var userSelection = await dialog.ExecuteModal();

      // Это раскроется во что-то типа такого
      var sc = SyncronizationContext.Current; // Захватили UI-ный контекст синхронизации
      var task = dialog.ExecuteModal();
      task.GetAwaiter().OnCompleted = () => {
            sc.Post(()=>{ // Исполняем continuation на UI-ном контексте
                  var userSelection = task.Result;
                  // Doing something with result
              });
      }    
  }
}
Отредактировано 20.11.2019 7:00 RushDevion . Предыдущая версия .
Re: Вопрос по async/await для UI
От: ksg71 Германия  
Дата: 20.11.19 07:55
Оценка:
Здравствуйте, Somescout, Вы писали:

S>Тема не по самому UI, а скорее по его архитектуре, поэтому создал в .NET, но если нужно перенесите в .NET GUI


S>Имеет ли право на жизнь такое использование async/await:


S>Модальный диалог:

S>
S>class ModalDialog<TItem> {

S>  private TaskCompletionSource<TItem> taskSource = new TaskCompletionSource<TItem>();

S>  public Task<Result> ExecuteModal() {
S>    this.Show();
S>    return taskSource.Task;
S>  }

S>  private void OkButtonClicked() {
S>    taskSource.SetResult(state);
S>  } 
S>}
S>


а вот тут вызов Show (ShowModal) по идее всегда блокирующий, иначе как модальность гарантировать.
и всегда этот таск уже будет возвращен выполненным

 public Task<Result> ExecuteModal() {
    this.Show();
    return taskSource.Task;
  }
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[2]: Вопрос по async/await для UI
От: Somescout  
Дата: 20.11.19 09:21
Оценка:
Здравствуйте, ksg71, Вы писали:

K>а вот тут вызов Show (ShowModal) по идее всегда блокирующий, иначе как модальность гарантировать.

K>и всегда этот таск уже будет возвращен выполненным

K>
K> public Task<Result> ExecuteModal() {
K>    this.Show();
K>    return taskSource.Task;
K>  }
K>


Он возвращает Task, который ожидает вызывающий метод. taskSource.SetResult вызывается когда пользователь завершает диалог, тогда ожидание диалога заканчивается и вызывающий метод получает его результат.
ARI ARI ARI... Arrivederci!
Re[2]: Вопрос по async/await для UI
От: Somescout  
Дата: 20.11.19 09:40
Оценка:
Здравствуйте, RushDevion, Вы писали:

S>>Имеет ли право на жизнь такое использование async/await:


RD>Имхо, работать-то будет, но целесообразность как-то сомнительна.

RD>Диалоговое окно — это по определению что-то блокирующее основной UI, какой здесь смысл в асинхронщине?
RD>Если только у тебе этих окошек открывается по несколько штук одновременно.

Смотрите, есть страница на Blazor, когда пользователь делает что-то, нужно запросить у него дополнительные данные, открывается модальный диалог (текущая страница не меняется), в нём запрашиваются данные. Когда диалог завершается, действие продолжается:
    private async void OnDoubleClick(DhcpServerLease lease)
    {
        var newIp = await ModalService.ShowModal<MM.Pages.Modals.AssignAddress, string>("Назначение нового IP-адреса", new Dictionary<string, object>
        {
            { "IpAddress", lease.Address },
        });

        if (newIp != default(string))
        {
            Device.ReassignHost(lease.Address, newIp);
            await Device.ReloadContextAsync();
        }
    }


RD>Дедлока не будет, но возможен unhandled exception, который не перехватится через try/catch (async void).

RD>Если бы сигнатура была такой async Task GetUserSelection, то дедлок был бы возможен, в зависимости от того, как вызывается метод.
Дело в том, что сам метод не содержит ожидания действий, поэтому ему самому асинхронность не нужна (код костыльный, всё ещё размышляю как его переработать).

  Немного кода
        public Task<ResultType> ShowModal<ComponentType, ResultType>(string title, Dictionary<string, object> parameters = null) where ComponentType : ComponentBase, IModalDialog<ResultType>
        {
            if (OnShowDialog == null)
            {
                throw new InvalidOperationException("ModalHost is not instantiated");
            }

            var taskSource = new TaskCompletionSource<ResultType>();
            IModalDialog<ResultType> modalDialog = null;

            var content = new RenderFragment(rf =>
            {
                int idx = 1;

                rf.OpenComponent(idx++, typeof(ComponentType));
                if (parameters != null)
                {
                    foreach (var param in parameters)
                    {
                        rf.AddAttribute(idx++, param.Key, param.Value);
                    }
                }
                rf.AddComponentReferenceCapture(idx++, component => { 
                    modalDialog = component as IModalDialog<ResultType>;
                    modalDialog.DialogHasChanged = DialogHasChanged;
                    modalDialog.CompleteModal = state => 
                        { 
                            taskSource.TrySetResult(state ? modalDialog.GetDialogResult() : default(ResultType));
                            OnClose?.Invoke();
                        };
                });
                rf.CloseComponent();
            });

            OnShowDialog(title,
                content,
                state => { taskSource.TrySetResult(state ? modalDialog.GetDialogResult() : default(ResultType)); },
                () => modalDialog?.ValidateDialog() ?? false
                );

            return taskSource.Task;
        }

Кстати, может подскажите можно ли сделать автоматический вывод вывод ResultType для этого метода? То есть чтобы можно было вызвать:
await ModalService.ShowModal<MM.Pages.Modals.AssignAddress>("Назначение нового IP-адреса", new Dictionary<string, object>{...});

вместо
await ModalService.ShowModal<MM.Pages.Modals.AssignAddress, string>("Назначение нового IP-адреса", new Dictionary<string, object>{...});

Класс определён как,
class AssignAddress: ComponentBase, IModalDialog<string> {...}

создавать экземпляр не вариант.

RD>Имхо, не обязательно (если, конечно GetUserSelection, из ui потока изначально вызывается).

Спасибо, проверю.
ARI ARI ARI... Arrivederci!
Re: Вопрос по async/await для UI
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.11.19 10:15
Оценка:
Здравствуйте, Somescout, Вы писали:

S>Тема не по самому UI, а скорее по его архитектуре, поэтому создал в .NET, но если нужно перенесите в .NET GUI


Вот тут случайно не оно?
Sync -> Async
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Вопрос по async/await для UI
От: ksg71 Германия  
Дата: 20.11.19 10:28
Оценка:
Здравствуйте, Somescout, Вы писали:

S>Здравствуйте, ksg71, Вы писали:


K>>а вот тут вызов Show (ShowModal) по идее всегда блокирующий, иначе как модальность гарантировать.

K>>и всегда этот таск уже будет возвращен выполненным

K>>
K>> public Task<Result> ExecuteModal() {
K>>    this.Show();
K>>    return taskSource.Task;
K>>  }
K>>


S>Он возвращает Task, который ожидает вызывающий метод. taskSource.SetResult вызывается когда пользователь завершает диалог, тогда ожидание диалога заканчивается и вызывающий метод получает его результат.


он вернет его когда this.Show() отработает, то есть ExecuteModal блокирующий и вовращает всегда завершенный таск
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[4]: Вопрос по async/await для UI
От: Somescout  
Дата: 20.11.19 10:32
Оценка:
Здравствуйте, ksg71, Вы писали:

K>>>
K>>> public Task<Result> ExecuteModal() {
K>>>    this.Show();
K>>>    return taskSource.Task;
K>>>  }
K>>>


S>>Он возвращает Task, который ожидает вызывающий метод. taskSource.SetResult вызывается когда пользователь завершает диалог, тогда ожидание диалога заканчивается и вызывающий метод получает его результат.


K>он вернет его когда this.Show() отработает, то есть ExecuteModal блокирующий и вовращает всегда завершенный таск


Внешний метод написан так:
    private async void OnDoubleClick(DhcpServerLease lease)
    {
        var newIp = await ModalService.ShowModal<MM.Pages.Modals.AssignAddress, string>("Назначение нового IP-адреса", new Dictionary<string, object>
        {
            { "IpAddress", lease.Address },
        });

        if (newIp != default)
        {
            ...
        }
    }


То есть возвращается задача, и внешний метод ожидает (асинхронно) её завершения. Завершается она по вызову taskSource.SetResult.
ARI ARI ARI... Arrivederci!
Re[2]: Вопрос по async/await для UI
От: Somescout  
Дата: 20.11.19 10:33
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Somescout, Вы писали:


S>>Тема не по самому UI, а скорее по его архитектуре, поэтому создал в .NET, но если нужно перенесите в .NET GUI


S>Вот тут случайно не оно?

S>Sync -> Async

Не, у меня пока до такого не дошло Пока с более простыми вещами разбираюсь.
ARI ARI ARI... Arrivederci!
Re[5]: Вопрос по async/await для UI
От: ksg71 Германия  
Дата: 20.11.19 10:41
Оценка:
Здравствуйте, Somescout, Вы писали:

S>Здравствуйте, ksg71, Вы писали:


K>>>>
K>>>> public Task<Result> ExecuteModal() {
K>>>>    this.Show();
K>>>>    return taskSource.Task;
K>>>>  }
K>>>>


S>>>Он возвращает Task, который ожидает вызывающий метод. taskSource.SetResult вызывается когда пользователь завершает диалог, тогда ожидание диалога заканчивается и вызывающий метод получает его результат.


K>>он вернет его когда this.Show() отработает, то есть ExecuteModal блокирующий и вовращает всегда завершенный таск


S>Внешний метод написан так:

S>
    private async void OnDoubleClick(DhcpServerLease lease)
S>    {
S>        var newIp = await ModalService.ShowModal<MM.Pages.Modals.AssignAddress, string>("Назначение нового IP-адреса", new Dictionary<string, object>
S>        {
S>            { "IpAddress", lease.Address },
S>        });

S>        if (newIp != default)
S>        {
S>            ...
S>        }
S>    }


S>То есть возвращается задача, и внешний метод ожидает (асинхронно) её завершения. Завершается она по вызову taskSource.SetResult.


формально выглядит так (для вызывающего кода), но внутри зависит от реализации Show
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[3]: Вопрос по async/await для UI
От: RushDevion Россия  
Дата: 20.11.19 10:44
Оценка:
S>Смотрите, есть страница на Blazor, когда пользователь делает что-то, нужно запросить у него дополнительные данные, открывается модальный диалог (текущая страница не меняется), в нём запрашиваются данные. Когда диалог завершается, действие продолжается:
S>
S>    private async void OnDoubleClick(DhcpServerLease lease)
S>    {
S>        var newIp = await ModalService.ShowModal<MM.Pages.Modals.AssignAddress, string>("Назначение нового IP-адреса", new Dictionary<string, object>
S>        {
S>            { "IpAddress", lease.Address },
S>        });

S>        if (newIp != default(string))
S>        {
S>            Device.ReassignHost(lease.Address, newIp);
S>            await Device.ReloadContextAsync();
S>        }
S>    }


Прежде всего, то что я писал выше про SyncronizationContext относится к WinForms/WPF/ASP.NET.
Я не работал с Blazor и не знаю его специфики.
Может там вообще никакой SyncronizationContext не используется.
Но почему не написать проще:
private void OnDoubleClick(DhcpServerLease lease)
{
  var newIp = new AssignAddressDialog().Show();
  if (newIp != default(string)) 
  {
    Device.ReassignHost(lease.Address, newIp);
    Device.ReloadContextAsync(); // Запустили таску и вышли из обработчика, пусть рефрешится в фоне
  }
}
Re[6]: Вопрос по async/await для UI
От: Somescout  
Дата: 20.11.19 11:04
Оценка:
Здравствуйте, ksg71, Вы писали:

K>формально выглядит так (для вызывающего кода), но внутри зависит от реализации Show


Show просто добавляет классы к элементу — сам о не блокирующий.
ARI ARI ARI... Arrivederci!
Re[4]: Вопрос по async/await для UI
От: Somescout  
Дата: 20.11.19 11:09
Оценка:
Здравствуйте, RushDevion, Вы писали:

RD>Прежде всего, то что я писал выше про SyncronizationContext относится к WinForms/WPF/ASP.NET.

RD>Я не работал с Blazor и не знаю его специфики.
RD>Может там вообще никакой SyncronizationContext не используется.
RD>Но почему не написать проще:
RD>
RD>private void OnDoubleClick(DhcpServerLease lease)
RD>{
RD>  var newIp = new AssignAddressDialog().Show();
RD>  if (newIp != default(string)) 
RD>  {
RD>    Device.ReassignHost(lease.Address, newIp);
RD>    Device.ReloadContextAsync(); // Запустили таску и вышли из обработчика, пусть рефрешится в фоне
RD>  }
RD>}
RD>


Потому что Show покажет диалог и сразу вернёт управление, не дожидаясь результата. Blazor это, по-сути, веб с наворотами (конкретнее — с вынесением логики на сервер), соответственно дождаться завершения диалога можно либо по коллбэку, либо через await.
ARI ARI ARI... Arrivederci!
Re: Вопрос по async/await для UI
От: Vladek Россия Github
Дата: 21.11.19 08:49
Оценка:
Здравствуйте, Somescout, Вы писали:

S>Тема не по самому UI, а скорее по его архитектуре, поэтому создал в .NET, но если нужно перенесите в .NET GUI


S>Имеет ли право на жизнь такое использование async/await:


Имеет.

S>То есть правильно ли ожидать закрытия диалога через async/await? Вроде по тестам дедлока не происходит. И если чуть усложнить вопрос: может ли произойти дедлок, если вызов taskSource.SetResult был сделан через синхронизацию с потоком UI из другого потока:


Дедлоки ты заметишь, а вот что у тебя UI-поток постоянно дергается твоим кодом для выполнения мелкой работы между асинхронными вызовами — нет. Или что у тебя твои асинхронные велосипеды допускают многократное вхождение: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/handling-reentrancy-in-async-apps

S>И, кстати, обязательно ли в таком случае переводить выполнение в поток UI, или же Task сам выберет правильный поток, из какого-бы потока не вызывали SetResult?


За это отвечает только вызывающий код. Любой код, вызываемый из UI-потока, должен избавляться от контекста синхронизации как можно раньше (ConfigureAwait(false)). Иначе твой код будет постоянно нагружать UI-поток ненужной работой. Чем меньше работы ты выполняешь через UI-поток, тем лучше.

Короче, тем меньше экспериментировать с async-await, тем меньше багов будет допущено, которых ты просто не заметишь. Впрочем, я именно так опыта набирался , но баги я всё-таки ловить умею.
Re: Вопрос по async/await для UI
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.11.19 10:15
Оценка:
Здравствуйте, Somescout, Вы писали:


S>То есть правильно ли ожидать закрытия диалога через async/await? Вроде по тестам дедлока не происходит. И если чуть усложнить вопрос: может ли произойти дедлок, если вызов taskSource.SetResult был сделан через синхронизацию с потоком UI из другого потока:

S>
S>  private async void OnMessageArrived() {  // Вызывается не из потока ui
S>    InvokeAsync(() => { // Передаём выполнение в поток UI
S>      taskCompletionSource.SetResult(someResult); // сигнализируем о завершении диалога
S>    });
S>  }
S>


S>И, кстати, обязательно ли в таком случае переводить выполнение в поток UI, или же Task сам выберет правильный поток, из какого-бы потока не вызывали SetResult?

На ксамарине во всяком случае после taskCompletionSource.SetResult(someResult) выполняется в том же потоке из которого вызывается этот SetResult.
Но так как у меня дальше шли await я особо и не парился.
https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
см AsyncProducerConsumerCollection<T>
Но вот что касается контекста синхронизации, то по идее
await dialog.ExecuteModal() должен выполнится в потоке UI если не добавлен ConfigureAwait(false);
https://ru.stackoverflow.com/questions/681382/Использование-configureawaitfalse
и солнце б утром не вставало, когда бы не было меня
Отредактировано 22.11.2019 14:36 Serginio1 . Предыдущая версия . Еще …
Отредактировано 22.11.2019 14:32 Serginio1 . Предыдущая версия .
Отредактировано 22.11.2019 14:29 Serginio1 . Предыдущая версия .
Re[7]: Вопрос по async/await для UI
От: ksg71 Германия  
Дата: 22.11.19 12:15
Оценка:
Здравствуйте, Somescout, Вы писали:

S>Здравствуйте, ksg71, Вы писали:


K>>формально выглядит так (для вызывающего кода), но внутри зависит от реализации Show


S>Show просто добавляет классы к элементу — сам о не блокирующий.



ну а если кто-нибудь захочет Wait вызвать?
на вашем таске ведь не написано что этого лучше не делать
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Отредактировано 22.11.2019 12:15 ksg71 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.