Попробую с асин c жду нить - PullRequest
0 голосов
/ 10 марта 2020

В конструкторе я хочу вызвать один тип метода:

private async Task OnLoadPrometDanKorisnikDatum

, и я хочу подождать, пока этот метод завершится sh, и у меня есть еще такой метод (3), и я хочу вызовите эти 3 метода в фоновом потоке и не ждите, пока он завершится sh, просто хотите дождаться первого метода. И я хочу, чтобы они выполняли параллель. У меня есть методы asyn c Task, и в конструкторе модели представления я называю это

OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, 
    DatumVrednost).Wait();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).Wait();

, если я не помещаю .Wait () в конец, программа не работает. Я вижу, что в режиме отладки они работают асинхронно, но затраченное время говорит мне, что они подчинены (время одного метода + время второго метода + ....). Может ли кто-нибудь помочь мне, это для меня очень глупо ...

1 Ответ

0 голосов
/ 11 марта 2020

Ответ

Лучший способ справиться с вашим сценарием - использовать async void.

Я рекомендую сначала прочитать раздел Explanation ниже, чтобы полностью ознакомиться с лучшими практиками по async void.

public MyConstructor()
{
    ExecuteAsyncMethods();
}

async void ExecuteAsyncMethods()
{ 
    try
    {
        await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
        await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
    }
    catch(Exception e)
    {
        //Handle Exception
    }
}

Объяснение

Многим C# разработчикам преподают "Никогда не используйте async void", но это один из немногих вариантов использования.

Да async void может быть опасным, и вот почему:

  • Не может await async avoid метод
  • Может привести к условиям гонки
  • Трудно поймать Exception, брошенный async void методами
    • Например, следующий try/catch блок не будет ловить Exception брошенный здесь:
public MyConstructor()
{
    try
    {
        //Cannot await `async void`
        AsyncVoidMethodWithException();
    }
    catch(Exception e)
    {
        //Will never catch the `Exception` thrown in  `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
    }

    //code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}

async void AsyncVoidMethodWithException()
{
    await Task.Delay(2000);
    throw new Exception();
}

При этом, пока мы заключаем содержимое всего нашего async void в try/catch блок, мы сможем перехватить исключение, например:

public MyConstructor()
{
    AsyncVoidMethodWithException();
}

async void AsyncVoidMethodWithException()
{
    try
    {
        await Task.Delay(2000);
        throw new Exception();
    }
    catch(Exception e)
    {
        //Exception will be caught and successfully handled 
    }
}

SafeFireAndForget

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

Это открытый код и также доступен на NuGet:

SafeFireAndForget

SafeFireAndForget позволяет нам безопасно выполнить Task, не блокируя вызывающий поток и не дожидаясь его завершения sh перед перемещением к следующей строке кода.

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

Однако я рекомендую скопировать / вставить его полный исходный код или добавление пакета NuGet в вашу библиотеку, чтобы получить более надежную реализацию

public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
    try
    {
        await task.ConfigureAwait(continueOnCapturedContext);
    }
    catch (TException ex) when (onException != null)
    {
        onException(ex);
    }
}

Использование SafeFireAndForget

Чтобы использовать SafeFireAndForget, добавьте его в свой вызов метода выглядит так:

OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();

Для обработки любого Exception, брошенного этим Task, используйте onException. Вот пример, который печатает Exception на консоли отладки:

OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));
...