Séquençage des tâches et réentrée : gestion des commandes séquentielles dans un environnement multithread
Dans les applications logicielles du monde réel, il est courant de rencontrer des scénarios où les tâches sont traitées séquentiellement et leur ordre d’exécution est critique. Le séquençage des tâches devient encore plus complexe lorsque les tâches peuvent arriver plus rapidement qu'elles ne peuvent être traitées et qu'elles sont réentrantes, ce qui signifie qu'elles peuvent s'exécuter simultanément en elles-mêmes.
Considérez le scénario suivant :
Traditionnellement, le problème de synchronisation ou d'asynchronisation peut être résolu en utilisant la méthode Task.Factory.StartNew avec le Paramètre TaskScheduler.FromCurrentSynchronizationContext() pour forcer l’exécution asynchrone. Cependant, cette approche n'est peut-être pas idéale si les gestionnaires de commandes sont conçus pour s'exécuter de manière synchrone en interne.
Pour relever ce défi, une classe personnalisée appelée AsyncOp peut être implémentée. AsyncOp gère le séquençage des tâches et les exigences de réentrée. Voici une version mise à jour de la classe AsyncOp avec des fonctionnalités supplémentaires :
class AsyncOp<T> { private Task<T> _pending = Task.FromResult(default(T)); private readonly object _lock = new object(); public Task<T> CurrentTask => _pending; public Task<T> RunAsync(Func<Task<T>> handler, bool useSynchronizationContext = false) { Task<Task<T>> task = null; lock (_lock) { var pending = _pending; Task<T> wrapper = async () => { // Await the previous task T prevResult = await pending; Console.WriteLine("\nPrevious task result: " + prevResult); // Start and await the handler return await handler(); }; task = new Task<Task<T>>(wrapper); var inner = task.Unwrap(); _pending = inner; } task.RunSynchronously(useSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current); return inner; } public async Task HandleCancelAsync() { Task pendingCopy = null; lock (_lock) { pendingCopy = _pending; } // Cancel previous tasks as long as they are not the latest (current) task if (pendingCopy != CurrentTask && pendingCopy != null) await ((Task)pendingCopy).ContinueWith(t => t.Dispose()); } public async Task HandleRestartAsync() { Task pendingCopy = null; lock (_lock) { pendingCopy = _pending; if (pendingCopy.IsCompleted && pendingCopy != null) _pending = Task.FromResult(default(T)); } } }
Cette version mise à jour fournit les améliorations suivantes :
À l'aide de cette classe AsyncOp mise à jour, l'exemple de code fourni dans la question peut être réécrit comme suit :
using System; using System.Threading.Tasks; namespace ConsoleApp { class Program { static async Task Main(string[] args) { var asyncOp = new AsyncOp<int>(); Func<int, Task<int>> handleAsync = async (arg) => { Console.WriteLine("This task arg: " + arg); // Delay the execution await Task.Delay(arg); return await Task.FromResult(arg); }; Console.WriteLine("Test #1..."); asyncOp.RunAsync(() => handleAsync(1000)); asyncOp.RunAsync(() => handleAsync(900)); asyncOp.RunAsync(() => handleAsync(800)); await asyncOp.CurrentTask; Console.WriteLine("\nPress any key to continue to test #2..."); Console.ReadLine(); asyncOp.RunAsync(() => { var handle100Task = handleAsync(100); asyncOp.RunAsync(() => handleAsync(200), true); return handle100Task; }); await asyncOp.CurrentTask; Console.WriteLine("\nPress any key to exit..."); Console.ReadKey(); } } }
Dans cette version mise à jour, la tâche handleAsync est forcée de s'exécuter de manière asynchrone à l'aide de la méthode Task.Delay pour démontrer les capacités de réentrance de AsyncOp.
Cette solution fournit un moyen robuste et efficace de gérer le séquençage et la réentrée des tâches, garantissant que les commandes sont exécutées dans l'ordre souhaité, quelle que soit la vitesse à laquelle elles arrivent.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!