【发布时间】:2021-03-29 19:46:05
【问题描述】:
我想我的代码有很多弱点,所以请随时分享任何想法。 顺便说一句,我的主要问题是,当我尝试执行以下操作时,最后,当我想等待所有任务(通过使用 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。
【问题讨论】:
-
标记为
async的方法通常会在某些时候执行await,但您的方法不会。所以发生的事情是该函数立即完成所有工作并返回一个已经完成的任务。对于Main,这并不重要。您可以从声明中删除async关键字。 -
真正的挑战是如何使用适当的异步/等待代码重构这个
ContinueWithgalore!
标签: c# async-await console-application task-parallel-library .net-5