繁体   English   中英

Task.WaitAll 的使用没有任何“等待”运算符导致警告 CS1998 此异步方法缺少“等待”运算符,将同步运行

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

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

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();
    }
}

完整来源,如果需要更多信息。 也许还值得一提的是,我使用的是 .NET 5.0。

它真的会让代码的任何部分同步运行,只是因为缺少“等待”运算符吗?

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

在旁注中, 使用await而不是ContinueWith

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM