在没有任何 'await' 运算符的情况下使用 Task.WaitAll 会导致警告 CS1998 此异步方法缺少 'await' 运算符并将 运行 同步

Usage of Task.WaitAll without any 'await' operators causing warning CS1998 This async method lacks 'await' operators and will run synchronously

我想我的代码有很多弱点,所以请随时分享任何想法。 顺便说一句,我的主要问题是,当我尝试执行以下操作时,最后,当我想等待所有任务(通过使用 Task.WaitAll)完成以检查是否有任何例外,它真的会因为缺少 'await' 运算符而使代码的任何部分同步到 运行 吗?

public class Program
{
    static bool mock = true;
    static readonly object s_lockSource = new object();
    static readonly CancellationTokenSource s_tokenSource = new CancellationTokenSource();
    static readonly CancellationToken s_token = s_tokenSource.Token;
    public static async Task Main()
    {
        var sw = new Stopwatch();
        sw.Start();

        IConfigurationRoot config = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json").Build();

        var logger = new LiteLogger(new MemoryStream(), config);
        logger.Log(LogLevel.Debug, "Fetching data...");

        var jsonHelper = new JsonHelper();
        IAsyncIO fileService = mock ? new MockAsyncIO() : new FileService(config, s_token);
        IAsyncService webService = mock ? new MockAsyncIO() : new WebService(config, s_token);

        var remoteFetchTask = webService.FetchAsync(InOutOptions.None);
        var localFetchTask = fileService.FetchAsync(InOutOptions.ForecastPath);
        var todaysFetchTask = fileService.FetchAsync(InOutOptions.TodaysPath);

        var parseRemoteDataTask = remoteFetchTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Remote fetch task completed!");
                    logger.Log(LogLevel.Info, "Parsing remote data...");
                }
                return jsonHelper.FromJsonAsync<Region[]>(task.Result);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var filterRemoteRegionsTask = parseRemoteDataTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Forecasts parsing task completed!");
                    logger.Log(LogLevel.Info, "Merging data...");
                }

                Region[] remoteRegions = parseRemoteDataTask.Result.Result;
                return Task.Run(() => new DataFilter(config).FilterRegions(remoteRegions));
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var parseLocalDataTask = localFetchTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Local fetch task completed!");
                    logger.Log(LogLevel.Info, "Parsing local data...");
                }
                return jsonHelper.FromJsonAsync<Region>(task.Result);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var parseTodaysDataTask = todaysFetchTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Today's fetch task completed!");
                    logger.Log(LogLevel.Info, "Parsing today's data...");
                }
                return jsonHelper.FromJsonAsync<Forecast[]>(task.Result);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);


        var mergeTask =
            Task.WhenAll(filterRemoteRegionsTask, parseLocalDataTask)
            .ContinueWith(_ =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Forecasts parsing task completed!");
                    logger.Log(LogLevel.Info, "Merging data...");
                }

                Region localInstance = parseLocalDataTask.Result.Result;
                Region remoteInstance = filterRemoteRegionsTask.Result.Result;

                var dm = new DataMerger();

                return Task.Run(() => dm.MergeRegions(localInstance, remoteInstance));
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var forecastsSerializationTask = mergeTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "MergeTask completed!");
                    logger.Log(LogLevel.Info, "Serializing forecasts data...");
                }

                Region newLocalInstance = task.Result.Result;
                return jsonHelper.ToJsonAsync(newLocalInstance);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var forecastsStoreTask = forecastsSerializationTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Forecasts serialization task completed!");
                    logger.Log(LogLevel.Info, "Storing forecasts data...");
                }

                var newLocalJson = task.Result.Result;

                return fileService.PersistAsync(newLocalJson, InOutOptions.ForecastPath);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);


        var todaysDataBuildTask =
            Task.WhenAll(parseTodaysDataTask, filterRemoteRegionsTask)
            .ContinueWith(_ =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Today's weather parsing task completed!");
                    logger.Log(LogLevel.Info, "Building today's data...");
                }

                Region remoteInstance = filterRemoteRegionsTask.Result.Result;
                Forecast[] todaysWeathers = parseTodaysDataTask.Result.Result;

                var tdb = new TodaysDataBuilder(remoteInstance, todaysWeathers);
                return Task.Run(() => tdb.Build());
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var todaysDataSerializationTask = todaysDataBuildTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Today's weather data build task completed!");
                    logger.Log(LogLevel.Info, "Serializing today's data...");
                }

                return jsonHelper.ToJsonAsync(task.Result.Result);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var todaysDataStoreTask = todaysDataSerializationTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Today's weather data serialization task completed!");
                    logger.Log(LogLevel.Info, "Storing today's data...");
                }

                return fileService.PersistAsync(task.Result.Result, InOutOptions.TodaysPath);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);

        var uiDataBuildTask = Task.WhenAll(mergeTask, todaysDataBuildTask)
            .ContinueWith(_ =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Antecedent tasks completed!");
                    logger.Log(LogLevel.Info, "Building UI data source...");
                }

                var newLocalInstance = mergeTask.Result.Result;
                var newTodaysDatas = todaysDataBuildTask.Result.Result;

                var usb = new UiSourceBuilder(newLocalInstance, newTodaysDatas);
                return Task.Run(() => usb.Build());
            }, TaskContinuationOptions.OnlyOnRanToCompletion);


        var uiDataStoreTask = uiDataBuildTask
            .ContinueWith(task =>
            {
                lock (s_lockSource)
                {
                    logger.Log(LogLevel.Success, "Building UI data completed!");
                    logger.Log(LogLevel.Info, "Saving UI data to source file...");
                }

                return fileService.PersistAsync(task.Result.Result, InOutOptions.ResultPath);
            }, TaskContinuationOptions.OnlyOnRanToCompletion);


        try
        {
            Task.WaitAll(new Task[]
                {
            localFetchTask,
            remoteFetchTask,
            todaysFetchTask,
            parseLocalDataTask,
            parseRemoteDataTask,
            parseTodaysDataTask,
            mergeTask,
            forecastsStoreTask,
            todaysDataStoreTask,
            uiDataStoreTask
                });

            sw.Stop();
            var overall = sw.Elapsed.TotalSeconds;
            logger.Log(LogLevel.Success, "All task completed!");
            logger.Log(LogLevel.Info, $"Finished in {overall} second{(overall != 1 ? "s" : "")}");
            if (overall <= 1)
                logger.Log(LogLevel.Warn, "This application is too fast :)");
        }
        catch (AggregateException ae)
        {
            foreach (var e in ae.Flatten().InnerExceptions)
                logger.Log(LogLevel.Error,
                    $"Exception has been thrown at: {e.StackTrace}" +
                    $"{Environment.NewLine}\t\t{e.Message}");
        }
        catch (Exception ex)
        {
            logger.Log(LogLevel.Fatal, ex.ToString());
        }

        Console.WriteLine("\nPress any key to continue...");
        Console.ReadKey();
        logger.Dispose();
    }
}

Full source,如果需要了解更多信息。 也许还值得一提的是我使用的是 .NET 5.0。

will it really make any part of the code to run synchronously, just because the lack of an 'await' operator?

是的。 Main 方法将同步 运行。这并不重要,因为它是 Main 方法,但如果您想 异步 等待任务完成,请使用 await Task.WhenAll 而不是 Task.WaitAll.异步方法还有一个额外的好处,它不会在 AggregateException.

中包装异常

旁注,use await instead of ContinueWith