Ключевые слова async/await в параллель.Еогеасп


У меня есть Timer:

var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));

Важной частью здесь является метод QueryReportTimerCallback.

У меня вот некоторые сомнения:

  • Это нормально, иметь async void способ? В целом это не так, но как я могу избежать этого, когда таймер обратного вызова делегата public delegate void TimerCallback(object state);?
  • Это нормально, чтобы ждать внутри Parallel.ForEach?

Способ QueryReportTimerCallback:

private async void QueryReportTimerCallback(object state)
{
    if (await semaphoreQueryReportSlim.WaitAsync(10))
    {
        try
        {
            if (machineConfigurations != null)
            {
                await Task.Factory.StartNew(() =>
                    Parallel.ForEach(machineConfigurations.Where(x => x.QueryReport), async (configuration) =>
                    {
                        if (configuration.IsConnectionValid)
                        {
                            var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
                            await QueryAReport(configuration, queryReport);
                        }
                    })
                );
            }
        }
        catch (Exception e)
        {
            logger.LogError(e, e.Message);
        }
        finally
        {
            semaphoreQueryReportSlim.Release();
        }
    }
}


17500
2
задан 9 февраля 2018 в 12:02 Источник Поделиться
Комментарии
1 ответ


Это нормально, чтобы иметь асинхронный метод пустоту?

Ссылка асинхронный/ждут - лучшие практики в асинхронное программирование

Как уже говорилось в ОП async void следует избегать, насколько это возможно.

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

Создать событие

event EventHandler QueryReportCallbackEvent = delegate { };

чтобы быть поднят обратного вызова таймера

private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}

Обработчик событий позволяет async void, Так что теперь можно спокойно делать

private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}


Это нормально, чтобы ждать внутри параллели.По каждому элементу?

Нет!!!!

Асинхронный лямда

async (configuration) => ...

будут преобразованы в async void что приводит нас обратно к тому, что было сказано в самом начале. async void плохо!!!

Вместо рефакторинга, что лямда в свой собственный метод, который возвращает Task

private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}

и это теперь позволит использовать Task.WhenAll со всеми конфигурациями машин, извлеченных из запроса.

var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));

await Task.WhenAll(tasks);

Это фактически снимает необходимость Paralell.ForEach.

Вот полный код того, что было описано выше.

//CTOR
public MyClass() {
QueryReportCallbackEvent += QueryReportCallbackEventHandler;
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}

event EventHandler QueryReportCallbackEvent = delegate { };

private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}

private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}

private async Task queryReportsCore() {
try {
if (machineConfigurations != null) {
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));

await Task.WhenAll(tasks);
}
} catch (Exception e) {
logger.LogError(e, e.Message);
} finally {
semaphoreQueryReportSlim.Release();
}
}

private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}

Наконец, обратите внимание, как функции были разбиты на более мелкие фрагменты, которые разрешены для уборщика, более легко читаемый код.

8
ответ дан 9 февраля 2018 в 11:02 Источник Поделиться