From 27c762cefbca59d9df962b1caafd9c751bcf5b0e Mon Sep 17 00:00:00 2001 From: Brett Hewitson Date: Fri, 20 May 2022 01:08:00 +1000 Subject: [PATCH] Server Monitor Changes - implemented the sequential server processing. --- .../Globalization/en-US/en-US.xaml | 2 +- src/ARKServerManager/Lib/ServerApp.cs | 13 +- .../Windows/ServerMonitorWindow.xaml.cs | 163 ++++++++++++++---- .../Globalization/en-US/en-US.xaml | 2 +- src/ConanServerManager/Lib/ServerApp.cs | 13 +- .../Windows/ServerMonitorWindow.xaml.cs | 163 ++++++++++++++---- 6 files changed, 274 insertions(+), 82 deletions(-) diff --git a/src/ARKServerManager/Globalization/en-US/en-US.xaml b/src/ARKServerManager/Globalization/en-US/en-US.xaml index 5ba3090f..e2725f72 100644 --- a/src/ARKServerManager/Globalization/en-US/en-US.xaml +++ b/src/ARKServerManager/Globalization/en-US/en-US.xaml @@ -473,7 +473,7 @@ Install the server. Update/Verify the server. - The process was cancelled by the user. You will need to the status of your servers. + The process was cancelled by the user. You will need to check the status of your servers. A process cancellation request has been sent. Server Update Error diff --git a/src/ARKServerManager/Lib/ServerApp.cs b/src/ARKServerManager/Lib/ServerApp.cs index 940eabfb..d6280abb 100644 --- a/src/ARKServerManager/Lib/ServerApp.cs +++ b/src/ARKServerManager/Lib/ServerApp.cs @@ -442,17 +442,20 @@ namespace ServerManagerTool.Lib SendMessage(ShutdownReason, cancellationToken); } - LogProfileMessage("Starting shutdown timer..."); - var minutesLeft = ShutdownInterval; if (ServerProcess == ServerProcessType.Stop) { - LogProfileMessage($"Server shutdown type is {ServerProcess}, shutdown timer cancelled."); + LogProfileMessage($"Server shutdown type is {ServerProcess}, shutdown timer will be skipped."); minutesLeft = 0; } - else if (!CheckForOnlinePlayers) + else { - LogProfileMessage("CheckForOnlinePlayers disabled, shutdown timer will not perform online player check."); + LogProfileMessage("Starting shutdown timer..."); + + if (!CheckForOnlinePlayers) + { + LogProfileMessage("CheckForOnlinePlayers disabled, shutdown timer will not perform online player check."); + } } while (minutesLeft > 0) diff --git a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs index c1c56f57..ff2fefed 100644 --- a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs +++ b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs @@ -68,6 +68,8 @@ namespace ServerManagerTool.Windows } } + private const int DELAY_PROCESSCOMPLETE = 5000; // 5 seconds + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly List Windows = new List(); @@ -1176,7 +1178,7 @@ namespace ServerManagerTool.Windows case ServerStatus.Uninstalled: case ServerStatus.Unknown: case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; } @@ -1209,24 +1211,45 @@ namespace ServerManagerTool.Windows } }; - var task = Task.Run(() => + var task = new Task(() => { + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_BackupRequested"), profile.ServerName)); + app.PerformProfileBackup(profile, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_BackupRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { @@ -1278,19 +1301,19 @@ namespace ServerManagerTool.Windows case ServerStatus.Stopping: case ServerStatus.Uninstalled: case ServerStatus.Unknown: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; case ServerStatus.Running: if (!restart) { - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; } break; case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); continue; } @@ -1325,27 +1348,48 @@ namespace ServerManagerTool.Windows } }; - var task = Task.Run(() => + var task = new Task(() => { + if (restart) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName)); + app.PerformProfileShutdown(profile, true, ServerUpdateType.None, false, false, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - if (restart) - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName)); - else - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { @@ -1395,16 +1439,23 @@ namespace ServerManagerTool.Windows switch (server.Runtime.Status) { - case ServerStatus.Initializing: case ServerStatus.Stopping: case ServerStatus.Stopped: case ServerStatus.Uninstalled: case ServerStatus.Unknown: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; + case ServerStatus.Initializing: + if (shutdown) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + } + break; + case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); continue; } @@ -1442,27 +1493,48 @@ namespace ServerManagerTool.Windows if (!shutdown) app.ShutdownInterval = 0; - var task = Task.Run(() => + var task = new Task(() => { + if (shutdown) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); + app.PerformProfileShutdown(profile, false, ServerUpdateType.None, false, false, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - if (shutdown) - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); - else - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { @@ -1519,11 +1591,11 @@ namespace ServerManagerTool.Windows case ServerStatus.Initializing: case ServerStatus.Stopping: case ServerStatus.Unknown: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); continue; } @@ -1558,24 +1630,45 @@ namespace ServerManagerTool.Windows } }; - var task = Task.Run(() => + var task = new Task(() => { + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_UpdateRequested"), profile.ServerName)); + app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, updateModsOnly ? ServerUpdateType.Mods : ServerUpdateType.ServerAndMods, false, false, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_UpdateRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { diff --git a/src/ConanServerManager/Globalization/en-US/en-US.xaml b/src/ConanServerManager/Globalization/en-US/en-US.xaml index 14aa154f..41d977d4 100644 --- a/src/ConanServerManager/Globalization/en-US/en-US.xaml +++ b/src/ConanServerManager/Globalization/en-US/en-US.xaml @@ -433,7 +433,7 @@ Install the server. Update/Verify the server. - The process was cancelled by the user. You will need to the status of your servers. + The process was cancelled by the user. You will need to check the status of your servers. A process cancellation request has been sent. Server Update Error diff --git a/src/ConanServerManager/Lib/ServerApp.cs b/src/ConanServerManager/Lib/ServerApp.cs index 804c3148..931261cc 100644 --- a/src/ConanServerManager/Lib/ServerApp.cs +++ b/src/ConanServerManager/Lib/ServerApp.cs @@ -438,17 +438,20 @@ namespace ServerManagerTool.Lib SendMessage(ShutdownReason, cancellationToken); } - LogProfileMessage("Starting shutdown timer..."); - var minutesLeft = ShutdownInterval; if (ServerProcess == ServerProcessType.Stop) { - LogProfileMessage($"Server shutdown type is {ServerProcess}, shutdown timer cancelled."); + LogProfileMessage($"Server shutdown type is {ServerProcess}, shutdown timer will be skipped."); minutesLeft = 0; } - else if (!CheckForOnlinePlayers) + else { - LogProfileMessage("CheckForOnlinePlayers disabled, shutdown timer will not perform online player check."); + LogProfileMessage("Starting shutdown timer..."); + + if (!CheckForOnlinePlayers) + { + LogProfileMessage("CheckForOnlinePlayers disabled, shutdown timer will not perform online player check."); + } } while (minutesLeft > 0) diff --git a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs index 85d2a342..644b5c59 100644 --- a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs +++ b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs @@ -68,6 +68,8 @@ namespace ServerManagerTool.Windows } } + private const int DELAY_PROCESSCOMPLETE = 5000; // 5 seconds + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly List Windows = new List(); @@ -1173,7 +1175,7 @@ namespace ServerManagerTool.Windows case ServerStatus.Uninstalled: case ServerStatus.Unknown: case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; } @@ -1206,24 +1208,45 @@ namespace ServerManagerTool.Windows } }; - var task = Task.Run(() => + var task = new Task(() => { + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_BackupRequested"), profile.ServerName)); + app.PerformProfileBackup(profile, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_BackupRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { @@ -1275,19 +1298,19 @@ namespace ServerManagerTool.Windows case ServerStatus.Stopping: case ServerStatus.Uninstalled: case ServerStatus.Unknown: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; case ServerStatus.Running: if (!restart) { - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; } break; case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); continue; } @@ -1322,27 +1345,48 @@ namespace ServerManagerTool.Windows } }; - var task = Task.Run(() => + var task = new Task(() => { + if (restart) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName)); + app.PerformProfileShutdown(profile, true, ServerUpdateType.None, false, false, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - if (restart) - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName)); - else - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { @@ -1392,16 +1436,23 @@ namespace ServerManagerTool.Windows switch (server.Runtime.Status) { - case ServerStatus.Initializing: case ServerStatus.Stopping: case ServerStatus.Stopped: case ServerStatus.Uninstalled: case ServerStatus.Unknown: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; + case ServerStatus.Initializing: + if (shutdown) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + } + break; + case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); continue; } @@ -1439,27 +1490,48 @@ namespace ServerManagerTool.Windows if (!shutdown) app.ShutdownInterval = 0; - var task = Task.Run(() => + var task = new Task(() => { + if (shutdown) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); + app.PerformProfileShutdown(profile, false, ServerUpdateType.None, false, false, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - if (shutdown) - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); - else - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) { @@ -1516,11 +1588,11 @@ namespace ServerManagerTool.Windows case ServerStatus.Initializing: case ServerStatus.Stopping: case ServerStatus.Unknown: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); continue; case ServerStatus.Updating: - AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName)); + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); continue; } @@ -1555,24 +1627,45 @@ namespace ServerManagerTool.Windows } }; - var task = Task.Run(() => + var task = new Task(() => { + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_UpdateRequested"), profile.ServerName)); + app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, updateModsOnly ? ServerUpdateType.Mods : ServerUpdateType.ServerAndMods, false, false, token); - Task.Delay(5000).Wait(); + Task.Delay(DELAY_PROCESSCOMPLETE).Wait(); AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName)); _currentProfileCommands.Remove(profile.ProfileId); }, token); tasks.Add(task); - - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_UpdateRequested"), profile.ServerName)); } try { - await Task.WhenAll(tasks); + if (processServersSequentially) + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + await task; + + await Task.Delay(sequentialProcessDelay * 1000, token); + } + } + else + { + foreach (var task in tasks) + { + token.ThrowIfCancellationRequested(); + + task.Start(); + } + await Task.WhenAll(tasks); + } } catch (OperationCanceledException) {