Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
Примерно так:

В form_Load:

new Task(async () =>
{
  await LoadSomeEntities1().ConfigureAwait(false);
  await LoadSomeEntities2().ConfigureAwait(false);
  await LoadSomeEntities3().ConfigureAwait(false);
}).RunSynchronously();


Форма загружается, всё отрабатывает, всё загружено. Никаких проблем!

В combobox_selectedIndexChanged:

LoadSomeEntities3().ConfigureAwait(false).GetAwaiter().GetResult();


А в самой функции:

async Task LoadSomeEntities3()
{
  trvTreeView.SuspendLayout();
  trvTreeView.BeginUpdate();
  trvTreeView.Nodes.Clear();

  await ReadEntitesFromDB().ConfigureAwait(false); // Но если здесь убрать await и добавить .GetAwaiter().GetResult(), то не виснет
  foreach(var entity in entities)
  {
    if(trvTreeView.InvokeRequired)
    {
      trvTreeView.Invoke((MethodDelegate) delegate // виснет здесь только при выборе значения в комбобоксе
      {
        trvTreeView.Nodes.Add(entity.Name);
      });
    }
    else
    {
       trvTreeView.Nodes.Add(entity.Name);
    }
  }

  if(trvTreeView.InvokeRequired)
  {
    trvTreeView.Invoke((MethodDelegate) delegate 
    {
      trvTreeView.EndUpdate();
      trvTreeView.ResumeLayout();
    }
  }
  else
  {
    trvTreeView.EndUpdate();
    trvTreeView.ResumeLayout();
  }
}


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

В обоих случаях InvokeRequired = true, соответственно всегда отрабатывает делегат.

Сообщение было отредактировано: 7 апр 21, 20:36
7 апр 21, 20:39    [22305685]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
Бариску Нацарство
await ReadEntitesFromDB().ConfigureAwait(false); // Но если здесь убрать await и добавить .GetAwaiter().GetResult(), то не виснет


потому что так делать нельзя, объяснять долго, нудно, всё это можно найти и почитать в документации.

непонятно зачем так делаете, это просто эксперименты?

https://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously
7 апр 21, 21:45    [22305699]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
что значит нельзя? так делают все и везде. в чём конкретно претензия?
и зачем мне ссылка на синхронное исполнение асинхронного вызова? синхронно-то оно и так работает.
7 апр 21, 23:08    [22305726]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
Бариску Нацарство
что значит нельзя?


то и значит.

Бариску Нацарство
так делают все и везде. в чём конкретно претензия?


new Task(async () =>
{
  await LoadSomeEntities1().ConfigureAwait(false);
  await LoadSomeEntities2().ConfigureAwait(false);
  await LoadSomeEntities3().ConfigureAwait(false);
}).RunSynchronously();


так даже в дремучем колхозе делать моветон.


Бариску Нацарство
и зачем мне ссылка на синхронное исполнение асинхронного вызова? синхронно-то оно и так работает.


у вас асинхронный метод

async Task LoadSomeEntities3()


зачем вы в этом методе асинхронный метод пытаетесь вызвать "синхронно"?

await ReadEntitesFromDB().ConfigureAwait(false); // Но если здесь убрать await и добавить .GetAwaiter().GetResult(), то не виснет


зачем убирать await и добавлять GetResult()? это во-первых неправильно, что как бы подтверждается тем, что "виснет", во-вторых.. зачем? это для чего?
7 апр 21, 23:23    [22305737]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
Бариску Нацарство,

и зачем вы везде .ConfigureAwait(false) добавляете? при чём там, где как раз этого делать нельзя.
7 апр 21, 23:24    [22305738]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
petalvik
Member

Откуда:
Сообщений: 708
Бариску Нацарство,

private async void Form1_LoadAsync(object sender, EventArgs e)
{
    await LoadSomeEntities1().ConfigureAwait(false);
    await LoadSomeEntities2().ConfigureAwait(false);
    await LoadSomeEntities3().ConfigureAwait(false);
}

Не нужно их оборачивать в Task и запускать синхронно.
При желании, можно написать так:

private async void Form1_LoadAsync(object sender, EventArgs e)
{
    var t1 = LoadSomeEntities1();
    var t2 = LoadSomeEntities2();
    var t3 = LoadSomeEntities3();
    await Task.WhenAll(t1, t2, t3);
}

Так все три метода будут выполняться одновременно, то есть общее выполнение будет быстрее. Но это если они не обращаются к одним и тем же объектам.


async Task LoadSomeEntities1()
{
    trvTreeView.SuspendLayout();
    trvTreeView.BeginUpdate();
    trvTreeView.Nodes.Clear();

    // Возвращаемся в тот же контекст синхронизации,
    // поэтому вызовы Invoke не нужны.
    await ReadEntitesFromDB();

    foreach (var entity in entities)
    {
        trvTreeView.Nodes.Add(entity.Name);
    }

    trvTreeView.EndUpdate();
    trvTreeView.ResumeLayout();
}

И это всё! Суть и красота асинхронности заключена в лаконичности, которую она позволяет.


private async void ComboBox_SelectedIndexChangedAsync(object sender, EventArgs e)
{
    await LoadSomeEntities3();            
}


Ничего не виснет, всё летает!

ЗЫ: у обработчиков событий в сигнатуре должно быть async.
7 апр 21, 23:58    [22305744]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
petalvik
ЗЫ: у обработчиков событий в сигнатуре должно быть async.


да, void async-и поддержали только ради событий WinForms
8 апр 21, 01:39    [22305754]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
Джон Скит из МС утверждает везде что на каждом асинхронном вызове должен висеть .ConfigureAwait(false).
Вы утверждаете, что это делает вызов синхронным. И кому верить?

> зачем убирать await и добавлять GetResult()? это во-первых неправильно, что как бы подтверждается тем, что "виснет", во-вторых.. зачем? это для чего?

чтобы не висло. Вы, кажется, всё перепутали: как "неправильно" не виснет. Как сейчас виснет.

Сообщение было отредактировано: 8 апр 21, 15:30
8 апр 21, 15:35    [22306023]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
petalvik
Бариску Нацарство,
async Task LoadSomeEntities1()
{
    trvTreeView.SuspendLayout();
    trvTreeView.BeginUpdate();
    trvTreeView.Nodes.Clear();

    // Возвращаемся в тот же контекст синхронизации,
    // поэтому вызовы Invoke не нужны.
    await ReadEntitesFromDB();

    foreach (var entity in entities)
    {
        trvTreeView.Nodes.Add(entity.Name);
    }

    trvTreeView.EndUpdate();
    trvTreeView.ResumeLayout();
}

И это всё! Суть и красота асинхронности заключена в лаконичности, которую она позволяет.

ЗЫ: у обработчиков событий в сигнатуре должно быть async.


Сделал всё, как Вы советуете выше. Вроде бы всё работает, только две проблемы остались:

await ReadEntitesFromDB(); виснет, если не добавить .ConfigureAwait(false)

и всё-таки добавление нод в trvTreeView по-прежнему бросает исключение об обращении из другой нити, если не оставить .Invoke((MethodDelegate)
8 апр 21, 16:22    [22306049]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
petalvik
Member

Откуда:
Сообщений: 708
Бариску Нацарство,

Вот этот код ошибочен:

private async void Form1_LoadAsync(object sender, EventArgs e)
{
    await LoadSomeEntities1().ConfigureAwait(false);
    await LoadSomeEntities2().ConfigureAwait(false);
    await LoadSomeEntities3().ConfigureAwait(false);
}

Здесь нужно убрать .ConfigureAwait(false). Потому что в этих методах происходит обращение к GUI-контролам.
Правильный код:

private async void Form1_LoadAsync(object sender, EventArgs e)
{
    await LoadSomeEntities1();
    await LoadSomeEntities2();
    await LoadSomeEntities3();
}


Теперь не будет бросать исключение.


Бариску Нацарство

await ReadEntitesFromDB(); виснет, если не добавить .ConfigureAwait(false)

Значит код в методе ReadEntitesFromDB() написан неправильно. Наверняка там тоже присобачено .GetAwaiter().GetResult() или что-то ещё. Покажи код.
8 апр 21, 16:37    [22306061]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
petalvik
Member

Откуда:
Сообщений: 708
Бариску Нацарство,

И ещё. Вызовы trvTreeView.SuspendLayout(); и trvTreeView.ResumeLayout(); не нужны. Если, конечно, в тривью не добавляются дочерние контролы вызовом trvTreeView.Controls.Add(...);
8 апр 21, 16:43    [22306064]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
petalvik
Бариску Нацарство,

Вот этот код ошибочен:


Спасибо, всё почистил, всё работает.
Объяснил бы ещё кто зачем тот самый Джон Скит везде пугает не использовать async void и настаивает чтобы на каждом await висел .ConfigureAwait(false). Я поначалу делал именно как Вы советуете, но меня затроллили и много лет заставляли добавлять .ConfigureAwait(false).
8 апр 21, 23:04    [22306217]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 22298
Бариску Нацарство
Объяснил бы ещё кто зачем тот самый Джон Скит везде пугает не использовать async void и настаивает чтобы на каждом await висел .ConfigureAwait(false). Я поначалу делал именно как Вы советуете, но меня затроллили и много лет заставляли добавлять .ConfigureAwait(false).
А не проще ли взять учебник и прочесть, что делает ConfigureAwait(false) и что такое контекст синхронизации и поток пользовательского интерфейса. Не перепечатывать же учебник на форум.
8 апр 21, 23:19    [22306223]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6149
hVostt
да, void async-и поддержали только ради событий WinForms

Почему это? События есть не только в винформс, а много ещё где - в т.ч. в областях, вообще никак не связанных с GUI:
var timer = new System.Timers.Timer(TimeSpan.FromSeconds(1).TotalMilliseconds)
{
  AutoReset = true,
  Enabled = false
};
timer.Elapsed += TimerElapsed;
timer.Start();
...
static async void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
  await Task.Delay(TimeSpan.FromSeconds(1));
  Console.WriteLine("Tick: {0}", DateTime.Now);
}
9 апр 21, 06:38    [22306255]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 22298
ТС ссылку-то не дал, а Джон Скит наверняка имел ввиду - не плодить собственные сигнатуры с async void. А от легаси никуда не денешься, потому это исключение. Да и про ConfugureAwait наверняка указывал те случаи, когда его нельзя применять. Но зачем вникать в детали - давай везде пихать.
9 апр 21, 09:35    [22306290]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
Бариску Нацарство
Джон Скит из МС утверждает везде что на каждом асинхронном вызове должен висеть .ConfigureAwait(false).
Вы утверждаете, что это делает вызов синхронным. И кому верить?


ничего он такого не утверждает.
9 апр 21, 10:13    [22306309]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
Сон Веры Павловны
Почему это? События есть не только в винформс, а много ещё где - в т.ч. в областях, вообще никак не связанных с GUI:


за пределами WinForms события лучше не использовать.
и вообще забыть про них как про страшный сон.
9 апр 21, 10:14    [22306310]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6149
Shocker.Pro
ТС ссылку-то не дал, а Джон Скит наверняка имел ввиду - не плодить собственные сигнатуры с async void.

Об этом пишет не только Скит (который, кстати говоря, давно уже в гугле работает). Вот, например: https://docs.microsoft.com/en-us/archive/blogs/ptorr/async-exceptions-in-c
Причина достаточно банальна: void не содержит информации о контексте выполнения задачи, т.к. это void. И поэтому с void есть вот такое:
static async Task Main()
{
  AppDomain.CurrentDomain.UnhandledException += (s, e)
    => Console.WriteLine("UnhandledException: {0}", e.ExceptionObject);
  TaskScheduler.UnobservedTaskException += (s, e) =>
    Console.WriteLine("UnobservedTaskException: {0}", e);
  Console.WriteLine("Running TestTask");
  try
  {
    await TestTask();
  }
  catch (Exception e)
  {
    Console.WriteLine("TestTask error: {0}", e);
  }
  Console.WriteLine("Running TestVoid");
  try
  {
    TestVoid(); // здесь await использовать нельзя
  }
  catch (Exception e)
  {
    Console.WriteLine("TestVoid error: {0}", e);
  }
  Console.WriteLine("done");
}

static async void TestVoid()
{
  await Task.Yield();
  throw new ApplicationException("TestVoid");
}

static async Task TestTask()
{
  await Task.Yield();
  throw new ApplicationException("TestVoid");
}


Running TestTask
TestTask error: System.ApplicationException: TestVoid
at test2.Program.<TestTask>d__2.MoveNext() in D:\Projects\.Net\_tests\test2\test2\Program.cs:line 54
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at test2.Program.<Main>d__0.MoveNext() in D:\Projects\.Net\_tests\test2\test2\Program.cs:line 26
Running TestVoid
done
UnhandledException: System.ApplicationException: TestVoid
at test2.Program.<TestVoid>d__1.MoveNext() in D:\Projects\.Net\_tests\test2\test2\Program.cs:line 48
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback,
Object state, Boolean preserveSyncCtx
)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback,
Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()

Unhandled Exception: System.ApplicationException: TestVoid
at test2.Program.<TestVoid>d__1.MoveNext() in D:\Projects\.Net\_tests\test2\test2\Program.cs:line 48
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback,
Object state, Boolean preserveSyncCtx
)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback,
Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
9 апр 21, 10:25    [22306315]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6149
hVostt
за пределами WinForms события лучше не использовать.
и вообще забыть про них как про страшный сон.

Интересно, как это можно реализовать, например, в случае SqlConnection.InfoMessage (таких примеров можно привести много)
9 апр 21, 10:31    [22306319]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
hVostt
Member

Откуда:
Сообщений: 19134
Сон Веры Павловны
Интересно, как это можно реализовать, например, в случае SqlConnection.InfoMessage (таких примеров можно привести много)


Согласен, есть легаси контракты, которые до сих пор поддерживаются.
От этих эвентов нужно максимально абстрагироваться и декорировать.
9 апр 21, 10:56    [22306328]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
hVostt
Бариску Нацарство
Джон Скит из МС утверждает везде что на каждом асинхронном вызове должен висеть .ConfigureAwait(false).
Вы утверждаете, что это делает вызов синхронным. И кому верить?


ничего он такого не утверждает.


Он именно так и утверждает в каждом из его 1000ч постов, которые он плодит на стэкэксчендже и в каждом ссылается на свои блог посты. Везде одно и то же: вешайте .ConfigureAwait(false) на каждый await чтобы не было дедлоков.
9 апр 21, 14:21    [22306469]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
Shocker.Pro
А не проще ли взять учебник и прочесть

А какой учебник?

Я 5 лет работал в компании, где, в соответствии утверждениям Скита, .ConfigureAwait(false) висел на каждом await, и мои пулл реквесты не одобряли, пока я не добавлял их. Объяснить, зачем они это требуют, правда тоже не могли.
9 апр 21, 14:24    [22306473]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
fkthat
Member

Откуда:
Сообщений: 4610
Бариску Нацарство
Везде одно и то же: вешайте .ConfigureAwait(false) на каждый await чтобы не было дедлоков.

Блин, а просто самому прочтитать доки о том, что делает .ConfigureAwait(...) сложно, что ли?
9 апр 21, 14:47    [22306500]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
Бариску Нацарство
Member

Откуда:
Сообщений: 11
fkthat
Бариску Нацарство
Везде одно и то же: вешайте .ConfigureAwait(false) на каждый await чтобы не было дедлоков.

Блин, а просто самому прочтитать доки о том, что делает .ConfigureAwait(...) сложно, что ли?


Configures the awaiter to await this Task<TResult>.

true to attempt to marshal the continuation back to the original context captured; otherwise, false.

И что это мне должно сказать?

Сообщение было отредактировано: 9 апр 21, 17:33
9 апр 21, 17:40    [22306634]     Ответить | Цитировать Сообщить модератору
 Re: От чего может виснуть этот код?  [new]
felix_ff
Member

Откуда: Moscow
Сообщений: 1714
Бариску Нацарство,

https://devblogs.microsoft.com/dotnet/configureawait-faq/
9 апр 21, 18:06    [22306659]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / WinForms, .Net Framework Ответить