[.NET][async][WinForms]
От: nikda  
Дата: 21.12.16 08:02
Оценка:
Есть консольный проект с использованием async await.
Его вывод:

Main(1): Th= '8', TaskId= ''
fAsync(1): Th= '8', TaskId= ''
DONE
ff(1): Th= '9', TaskId= '1'
ff(2): Th= '9', TaskId= '1'
fAsync(2): Th= '9', TaskId= ''


Т.е. асинхронная операция выполняется в отдельном потоке (8). И завершается (тело метода после await) тоже в нём (9).

А в WinForms-проекте вывод такой:

Main(1): Th= '9', TaskId= ''
fAsync(1): Th= '9', TaskId= ''
ff(1): Th= '6', TaskId= '1'
DONE
ff(2): Th= '6', TaskId= '1'
fAsync(2): Th= '9', TaskId= ''


Т.е. асинхронная операция выполняется в отдельном потоке (6). А завершается (тело метода после await) в основном (9).

Как это так получается?

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main(1): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);

            fAsync();

            Console.WriteLine("DONE");
            Console.ReadKey();
        }

        private static async void fAsync()
        {
            Console.WriteLine("fAsync(1): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);

            await ff();

            Console.WriteLine("fAsync(2): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
        }

        private static Task ff()
        {
            Task t = new Task(() =>
            {
                Console.WriteLine("ff(1): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                Thread.Sleep(5000);
                Console.WriteLine("ff(2): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
            });
            t.Start();

            return t;
        }
    }


        private void button1_Click(object sender, EventArgs e)
        {
            Debug.WriteLine("Main(1): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);

            fAsync();

            Debug.WriteLine("DONE");
        }

        private static async void fAsync()
        {
            Debug.WriteLine("fAsync(1): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);

            await ff();

            Debug.WriteLine("fAsync(2): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
        }

        private static Task ff()
        {
            Task t = new Task(() =>
            {
                Debug.WriteLine("ff(1): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                Thread.Sleep(5000);
                Debug.WriteLine("ff(2): Th= '{0}', TaskId= '{1}'", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
            });
            t.Start();

            return t;
        }
Отредактировано 23.12.2016 3:16 VladD2 . Предыдущая версия .
Re: [.NET][async][WinForms]
От: _Raz_  
Дата: 21.12.16 08:17
Оценка: 24 (1) +2
Здравствуйте, nikda, Вы писали:

N>Как это так получается?


Эта магия называется SynchronizationContext

Parallel Computing — It's All About the SynchronizationContext
ExecutionContext vs SynchronizationContext
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.12.16 08:45
Оценка:
Здравствуйте, nikda, Вы писали:
win10 universal app, async задержка


Используй
await ff().ConfigureAwait(false); ;
и солнце б утром не вставало, когда бы не было меня
Re: [.NET][async][WinForms]
От: alexzzzz  
Дата: 21.12.16 09:58
Оценка:
Здравствуйте, nikda, Вы писали:

N>Как это так получается?


У консольных приложений отсутствует контекст синхронизации.
https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
Re[2]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.12.16 10:08
Оценка:
Здравствуйте, Serginio1, Вы писали:

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

S>win10 universal app, async задержка


S>Используй

S>
S>await ff().ConfigureAwait(false); ;
S>


И за, что минус?
и солнце б утром не вставало, когда бы не было меня
Re[3]: [.NET][async][WinForms]
От: alexzzzz  
Дата: 21.12.16 10:21
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> И за, что минус?


ConfigureAwait ничего не даст. Без контекста синхронизации у потока нет простого способа вернуть управление в этот поток.
Отредактировано 21.12.2016 10:23 alexzzzz . Предыдущая версия .
Re[4]: [.NET][async][WinForms]
От: Lexey Россия  
Дата: 21.12.16 10:51
Оценка:
Здравствуйте, alexzzzz, Вы писали:

A>ConfigureAwait ничего не даст.


Даст. Контекст не будет использоваться.

A>Без контекста синхронизации у потока нет простого способа вернуть управление в этот поток.


А с чего ты решил, что приведенный кусок кода написан ради этого?
"Будь достоин победы" (c) 8th Wizard's rule.
Re[4]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.12.16 10:59
Оценка:
Здравствуйте, alexzzzz, Вы писали:

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


S>> И за, что минус?


A>ConfigureAwait ничего не даст. Без контекста синхронизации у потока нет простого способа вернуть управление в этот поток.

Так у него как есть контекст синхронизации. Для WinForms это WindowsFormsSynchronizationContext

Смотри внимательно исходное сообщение

А в WinForms-проекте вывод такой:


Даже если нет Контекта синхронизации, я могу его создать


if (SynchronizationContext.Current == null)
            SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
 
        Sc = SynchronizationContext.Current;


Пример для синхронизации методов в 1С

.NET(C#) для 1С. Динамическая компиляция класса обертки для использования .Net событий в 1С через ДобавитьОбработчик или ОбработкаВнешнегоСобытия

И там я в эту ловушку попался.
По умолчанию в библиотеках нужно везде ставить .ConfigureAwait(false);

Так как по умолчанию к сожалению true. true нужно только для модификации значений контролов и можно для этого и вызывать .ConfigureAwait(true);
и солнце б утром не вставало, когда бы не было меня
Отредактировано 21.12.2016 11:10 Serginio1 . Предыдущая версия . Еще …
Отредактировано 21.12.2016 11:00 Serginio1 . Предыдущая версия .
Re[5]: [.NET][async][WinForms]
От: _Raz_  
Дата: 21.12.16 11:47
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>По умолчанию в библиотеках нужно везде ставить .ConfigureAwait(false);

S>true нужно только для модификации значений контролов

Не нужно давать таких советов. В многопоточности и асинхронности взаимодействие с GUI отнюдь не единственная и не самая сложная задача.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[6]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.12.16 11:52
Оценка:
Здравствуйте, _Raz_, Вы писали:

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


S>>По умолчанию в библиотеках нужно везде ставить .ConfigureAwait(false);

S>>true нужно только для модификации значений контролов

_R_>Не нужно давать таких советов. В многопоточности и асинхронности взаимодействие с GUI отнюдь не единственная и не самая сложная задача.

Угу, только вокруг этого дедлоков постоянно идут вопросы везде связанного с контекстом синхронизации.
Как раз и беда в том, что создавая библиотеку, нужно учитывать, что она может быть вызвана из контекста синхронизации.
Просто создавая библиотеку нужно везде с await добавлять .ConfigureAwait(false). И даже там, где есть контекст синхронизации, если не нужно переключаться на поток GUI.
И в чем это вредный совет?
и солнце б утром не вставало, когда бы не было меня
Отредактировано 21.12.2016 11:59 Serginio1 . Предыдущая версия .
Re[7]: [.NET][async][WinForms]
От: _Raz_  
Дата: 21.12.16 12:09
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> И в чем это вредный совет?


Люблю себя цитировать: "В многопоточности и асинхронности взаимодействие с GUI отнюдь не единственная и не самая сложная задача."
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[8]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.12.16 12:13
Оценка:
Здравствуйте, _Raz_, Вы писали:

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


S>> И в чем это вредный совет?


_R_>Люблю себя цитировать: "В многопоточности и асинхронности взаимодействие с GUI отнюдь не единственная и не самая сложная задача."

При этом данный вопрос относится к GUI.
Еще раз в чем вредный совет про .ConfigureAwait(false)?
Учитывая, что твоя библиотека может использоваться в GUI
и солнце б утром не вставало, когда бы не было меня
Отредактировано 21.12.2016 12:15 Serginio1 . Предыдущая версия .
Re[9]: [.NET][async][WinForms]
От: TK Лес кывт.рф
Дата: 22.12.16 06:17
Оценка:
Здравствуйте, Serginio1, Вы писали:

_R_>>Люблю себя цитировать: "В многопоточности и асинхронности взаимодействие с GUI отнюдь не единственная и не самая сложная задача."

S> При этом данный вопрос относится к GUI.
S> Еще раз в чем вредный совет про .ConfigureAwait(false)?

Если есть код который может выполняться в отдельном планировщике то и запускать его там стоит явно. А вот распихивать по коду ConfigureAwait(false) в надежде так то, что оно по какой-то удаче продолжит выполняться где-то еще — решение так себе.

S> Учитывая, что твоя библиотека может использоваться в GUI


Ну это до первого Callback в GUI
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[10]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.12.16 07:13
Оценка:
Здравствуйте, TK, Вы писали:


S>> Еще раз в чем вредный совет про .ConfigureAwait(false)?


TK>Если есть код который может выполняться в отдельном планировщике то и запускать его там стоит явно. А вот распихивать по коду ConfigureAwait(false) в надежде так то, что оно по какой-то удаче продолжит выполняться где-то еще — решение так себе.


.ConfigureAwait(false) гарантирует, что будет игнорироваться Контекст синхронизации. Только и всего.



S>> Учитывая, что твоя библиотека может использоваться в GUI


TK>Ну это до первого Callback в GUI


И в чем проблема? .ConfigureAwait(false) гарантирует что будет игнорироваться Контекст синхронизации, и не будет переключаться на поток GUI.
Просто по умолчанию .ConfigureAwait(true), что приводит к дедлокам
и солнце б утром не вставало, когда бы не было меня
Отредактировано 22.12.2016 9:01 Serginio1 . Предыдущая версия .
Re[3]: [.NET][async][WinForms]
От: Sinix  
Дата: 22.12.16 07:22
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S> И за, что минус?

В смысле минус? Это ж просто "не согласен" кнопка. Обозначение неудачное, но все привыкли.

Не согласен, т.к. всё советы в духе "просто используй X" без детального изучения матчасти принесут ещё больше вреда. Добавь в WPF app вот этот код:
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RunAsync(() => button.Content = "x_X")
                .ContinueWith(t => MessageBox.Show("Completed:" + t.IsCompleted));
        }

        private async Task RunAsync(Action onNext)
        {
            await Task.Delay(1).ConfigureAwait(false);
            onNext();
        }


и попробуй найти все места, где что-то пошло не так. Там их как минимум шесть. Список —
  спойлер
по ссылке
Re[11]: [.NET][async][WinForms]
От: _Raz_  
Дата: 22.12.16 07:26
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>Просто по умолчанию .ConfigureAwait(true), что приводит к дедлокам


Да не это приводит к дедлокам, а попытка использовать асинхронные методы в синхронном режиме.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[4]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.12.16 07:33
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Не согласен, т.к. всё советы в духе "просто используй X" без детального изучения матчасти принесут ещё больше вреда. Добавь в WPF app вот этот код:

S>
S>        private void Button_Click(object sender, RoutedEventArgs e)
S>        {
S>            RunAsync(() => button.Content = "x_X")
S>                .ContinueWith(t => MessageBox.Show("Completed:" + t.IsCompleted));
S>        }

S>        private async Task RunAsync(Action onNext)
S>        {
S>            await Task.Delay(1).ConfigureAwait(false);
S>            onNext();
S>        }
S>


S>и попробуй найти все места, где что-то пошло не так. Там их как минимум шесть. Список —

S>
  спойлер
S>по ссылке


Ны дык значения контролов нужно устанавливать в потоке GUI
Поэтому правильно делать так.

private async Task Button_Click(object sender, RoutedEventArgs e)
        {
            await RunAsync().ConfigureAwait(true);
                        button.Content = "x_X";

            MessageBox.Show("Completed:" + t.IsCompleted));
        }

А вот если в моем варианте RunAsync сделать
await Task.Delay(1);

без .
ConfigureAwait(false);

то получим дедлок
и солнце б утром не вставало, когда бы не было меня
Отредактировано 22.12.2016 9:01 Serginio1 . Предыдущая версия . Еще …
Отредактировано 22.12.2016 7:39 Serginio1 . Предыдущая версия .
Отредактировано 22.12.2016 7:39 Serginio1 . Предыдущая версия .
Re[12]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.12.16 07:36
Оценка:
Здравствуйте, _Raz_, Вы писали:


S>>Просто по умолчанию .ConfigureAwait(true), что приводит к дедлокам


_R_>Да не это приводит к дедлокам, а попытка использовать асинхронные методы в синхронном режиме.


Да? Весь интернет ими усыпан. Именно для GUI по умолчанию асинхронные методы превращаются в синхронные там где это и не надо, по тому, что по умолчанию .ConfigureAwait(true).
и солнце б утром не вставало, когда бы не было меня
Отредактировано 22.12.2016 9:00 Serginio1 . Предыдущая версия .
Re[5]: [.NET][async][WinForms]
От: TK Лес кывт.рф
Дата: 22.12.16 07:38
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S> Ны дык значения контролов нужно устанавливать в потоке GUI

S>Поэтому правильно делать так.

S>private async Task Button_Click(object sender, RoutedEventArgs e)

S> {
S> await RunAsync().ConfigureAwait(true);
S> button.Content = "x_X";

S> MessageBox.Show("Completed:" + t.IsCompleted));

S> }

Правильно было вообще не трогать ConfigureAwait()
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[6]: [.NET][async][WinForms]
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.12.16 07:42
Оценка: +1 -1
Здравствуйте, TK, Вы писали:

S>>private async Task Button_Click(object sender, RoutedEventArgs e)

S>> {
S>> await RunAsync().ConfigureAwait(true);
S>> button.Content = "x_X";

S>> MessageBox.Show("Completed:" + t.IsCompleted));

S>> }

TK>Правильно было вообще не трогать ConfigureAwait()


В данном случае да, так как ConfigureAwait(true); по умолчанию.
По мне так лучше по умолчанию использовать как раз ConfigureAwait(false); а в GUI как раз явно использовать ().ConfigureAwait(true);
там где нужно переключаться на поток GUI
и солнце б утром не вставало, когда бы не было меня
Отредактировано 22.12.2016 8:40 Serginio1 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.