Note: les exemples sont développés avec Snippet Compiler.
Comme précisé dans l'article, l'utilisation de RegisterWaitForSingleObject convient particulièrement aux tâches qui s'attendent les unes les autres.
Rien de tel qu'un State Diagram pour illustrer cet exemple... et pourquoi pas en préparant du café.
Petite subtilité par rapport au diagramme:
Le état "Tenir le café au chaud" perdurera tant que l'état (la routine de traitement) ne reçoit pas le signal que quelqu'un prend du café (cfr: le ManualResetEvent "I_Take_Coffee").En quittant son état "Tenir le café au chaud", la routine de traitement lancera l'ordre d'extinction automatique de la machine a café (cfr: le AutoResetEvent "AutoTurnOffCoffeeMachine")
Transformation du diagramme en code:
Chacun des états est implémenté dans un Worker (routine de traitement). A savoir:- DoWork_FillingCoffeeMachine
- DoWork_CookCoffee
- DoWork_KeepCoffeeWarm
- DoWork_TurnOffCoffeeMachine
- Start - Permet de démarrer la State Machine
- Filled - Permet de savoir que le réservoir est remplis
- Cooked - Permet de savoir que la café est passé/fait.
- AutoTurnOffCoffeeMachine - Permet de savoir qu'il faut éteindre la machine.
- Exit - Permet au thread principal de savoir que l'on est sorti de la State Machine.
- I_Take_Coffee - Permet au worker DoWork_KeepCoffeeWarm d'être informé que l'utilisateur a prit du café.
Le signal "Cooked" ne peut donc pas être implémenté à l'aide d'un AutoResetEvent (sinon, un seul des deux threads pourrait acquerir le signal) mais à l'aide un ManualResetEvent.
Résultat de l'exécution:
Filling the Coffee machine Glou..Glou... Glou..Glou... Glou..Glou... Glou..Glou... Glou..Glou... Filled! (Send Filled signal) Cooking the Coffee Heating...Heating... Heating...Heating... Heating...Heating... Cooked! (Send Cooked signal) The user is allowed to take some coffee Press ENTER to take some coffee Keeping the coffee warm Warm...Warm... Keeping the coffee warm Warm...Warm... Keeping the coffee warm Warm...Warm... Keeping the coffee warm Warm...Warm... Keeping the coffee warm Warm...Warm... Keeping the coffee warm Yes! User did take coffee. (Send TurnOff signal) Turning off the Coffee machine Turned off Exit signal received on main thread.
Le code source:
Fichier: Threading_ThreadPool_RegisterWaitForSingleObject.csusing System; using System.Collections.Generic; using System.Threading; public class MyClass { // To signal for Start the whole Coffee process static AutoResetEvent Start = new AutoResetEvent(false); // To Signal that the coffee machine is full of water static AutoResetEvent Filled = new AutoResetEvent(false); // To signal that coffee is cooked // Main Thread and DoWork_KeepCoffeeWarn is listening this event. // AutoResetEvent cannot be user otherwise only one thread will acquire the signal. static ManualResetEvent Cooked = new ManualResetEvent(false); // To signal that the Coffee machine can be auto turned off // (because the user did take some coffee) static AutoResetEvent AutoTurnOffCoffeeMachine = new AutoResetEvent(false); // The process did complete, Main thread can exit. static AutoResetEvent Exit = new AutoResetEvent(false); // Main thread signaling DoWork_KeepCoffeeWarn that user takes coffee // (pressed Enter). The DoWork_KeepCoffeeWarn can stop to wan static ManualResetEvent I_Take_Coffee = new ManualResetEvent(false); public static void RunSnippet() { //=== Register ThreadPoll workers === ThreadPool.RegisterWaitForSingleObject( Start, DoWork_FillingCoffeeMachine, null, -1, false ); ThreadPool.RegisterWaitForSingleObject( Filled, DoWork_CookCoffee, null, -1, false ); //NB: Execute DoWork_KeepCoffeeWarm only ONCE because linked to ManualResetEvent, // Otherwise, it would be executed an infinitely of times. ThreadPool.RegisterWaitForSingleObject( Cooked, DoWork_KeepCoffeeWarm, null, -1, true ); ThreadPool.RegisterWaitForSingleObject( AutoTurnOffCoffeeMachine, DoWork_TurnOffCoffeeMachine, null, -1, false ); //=== Start ==== Start.Set(); // Wait that the coffee is cooked before allowing the user to take coffee Cooked.WaitOne(); // Wait Carriage Return and Signal that I did take coffee WL("The user is allowed to take some coffee"); WL( "Press ENTER to take some coffee" ); RL(); I_Take_Coffee.Set(); // Wait that DoWork_TurnOffCoffeeMachine signal the Exit flag. Exit.WaitOne(); WL( "Exit signal received on main thread." ); } public static void DoWork_FillingCoffeeMachine( object data, Boolean timedOut){ WL("Filling the Coffee machine" ); for( int i = 0; i < 5; i++ ){ WL( " Glou..Glou..." ); Thread.Sleep( TimeSpan.FromSeconds( 1 ) ); } WL(" Filled! (Send Filled signal)" ); Filled.Set(); } public static void DoWork_CookCoffee( object data, Boolean timedOut ){ WL("Cooking the Coffee" ); for( int i = 0; i < 3; i++ ){ WL( " Heating...Heating..." ); Thread.Sleep( TimeSpan.FromSeconds( 1.5 ) ); } WL(" Cooked! (Send Cooked signal)" ); Cooked.Set(); } public static void DoWork_KeepCoffeeWarm( object data, Boolean timedOut ){ WL("Keeping the coffee warm" ); while( true ){ WL( " Warm...Warm... Keeping the coffee warm" ); // Simulate 0.5 second of processing Thread.Sleep( TimeSpan.FromMilliseconds( 500 ) ); // Wait 2 seconds for signal // Break the while loop thread is signaled by user taking the coffee if( I_Take_Coffee.WaitOne( TimeSpan.FromSeconds(2) ) ) break; } WL(" Yes! User did take coffee. (Send TurnOff signal)" ); AutoTurnOffCoffeeMachine.Set(); } public static void DoWork_TurnOffCoffeeMachine( object data, Boolean timedOut ){ WL("Turning off the Coffee machine" ); Thread.Sleep( TimeSpan.FromSeconds( 1 ) ); WL("Turned off" ); Exit.Set(); } .... }
Aucun commentaire:
Enregistrer un commentaire