简体   繁体   中英

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

I guess my code has many weak points, so please feel free to share any thoughts. My main question btw, is that when I'm trying to do the following, and at the end, when I'd like to wait all my tasks (by using Task.WaitAll) to get completed to examine if there were any exceptions, will it really make any part of the code to run synchronously, just because the lack of an 'await' operator?

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 , if needed for further information. Maybe it's also worth mentioning that I'm using .NET 5.0.

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

Yes. The Main method will run synchronously. This won't really matter because it's the Main method, but if you want to asynchronously wait for the tasks to complete, use await Task.WhenAll instead of Task.WaitAll . The asynchronous approach has an additional benefit in that it doesn't wrap exceptions in AggregateException .

On a side note, use await instead of ContinueWith .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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