2014-06-07 13 views
5
class Program 
{ 
    static IEnumerable<site> list = Enumerable.Range(1, 10).Select(i => new site(i.ToString())); 

    static void Main(string[] args) 
    { 
     startup(); 
     Console.ReadKey(); 
    } 

    static public void startup() 
    { 
     router.cts = new CancellationTokenSource(); 

     foreach (var s in list) 
     { 
      update(s); 
     } 
    } 

    async static public void update(site s) 
    { 
     try 
     { 
      while (true) 
      { 
       await s.Refresh(); 
       if (site.count % 4 == 0) 
       { 
        Console.WriteLine("Reseting Queue"); 
        router.cts.Cancel(); 
       } 
      } 
     } 
     catch (OperationCanceledException) 
     { 
      Console.WriteLine("Canceled"); 
      startup(); 
     } 
    } 
} 

class router 
{ 
    public static SemaphoreSlim ss = new SemaphoreSlim(1); 
    public static CancellationTokenSource cts { get; set; } 


} 

class site 
{ 
    public static int count = 0; 
    public string sitename {get; set;} 

    public site(string s) 
    { 
     sitename = s; 
    } 

    async public Task Refresh() 
    { 
     await router.ss.WaitAsync(router.cts.Token); 
     //Console.WriteLine("{0}:: Start Refreshing ", sitename); 
     await Task.Delay(1000); 

     Console.WriteLine("{0}:: Done Refreshing ", sitename); 
     count++; 
     router.ss.Release(); 
    } 


} 

Sto tentando di simulare un modello che avvia un ciclo infinito che simula un aggiornamento costante di un sito. Sto imitando questo con il modulo. In teoria mi piacerebbe che questo annulasse tutti i compiti accodati dal semaforo e riavvia la coda dall'inizio, ma sembra che non lo faccia. Qualcuno potrebbe commentare la mia logica e il mio schema?Token di cancellazione semaforo

l'uscita in questo momento appare così ::

1:: Done Refreshing 
2:: Done Refreshing 
3:: Done Refreshing 
4:: Done Refreshing 
Reseting Queue 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
5:: Done Refreshing 
1:: Done Refreshing 
2:: Done Refreshing 
3:: Done Refreshing 
Reseting Queue 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
4:: Done Refreshing 
5:: Done Refreshing 
6:: Done Refreshing 
7:: Done Refreshing 
Reseting Queue 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
Canceled 
8:: Done Refreshing 
9:: Done Refreshing 
10:: Done Refreshing 
1:: Done Refreshing 
Reseting Queue 
Canceled 
+0

Vuoi che i siti vengano aggiornati in parallelo o uno per uno? – i3arnon

+0

Voglio che si aggiornino in parallelo ma il semaforo serve a simulare un codice che deve avere un blocco su di esso. – cubesnyc

+0

@YevgenyYurgenson, è un seguito alla tua [domanda precedente] (http://stackoverflow.com/q/24020740/1768303)? Qual è il punto di avere 'SemaphoreSlim (1)'? Non vuoi avere più di 1 operazione allo stesso tempo? – Noseratio

risposta

2

Così, ho diversi commenti, alcuni sono bug e alcuni solo suggerimenti:

  1. Se posso, preferisco il controllo per la cancellazione e terminare l'operazione, e non lanciare un'eccezione.
  2. update è async void, qualcosa che non dovrebbe quasi mai accadere al di fuori di un gestore di eventi. Non è possibile osservare errori e può causare una miriade di errori.
  3. per parallelizzare gli aggiornamenti del sito, il fuoco tutti gli aggiornamenti e attendono solo una volta con Task.WhenAll
  4. Si sta chiamando startup quando qualsiasi operazione viene annullata. Ciò significa che quando annulli 5 dei 10 aggiornamenti del sito, stai lanciando 50 nuovi aggiornamenti del sito. Questo non è necessario.
  5. Il passaggio di un CancellationToken a SemaphoreSlim.WaitAsync osserva solo la cancellazione durante l'attesa del semaforo. qualsiasi operazione già in esecuzione non si fermerà. E 'davvero questo il tuo intento? Un approccio migliore sarebbe quello di controllare il token durante l'aggiornamento. Questo può essere simulato passando il token all'operazione Task.Delay.

Ecco come lo avrei fatto:

class Program 
{ 
    static IEnumerable<site> list = Enumerable.Range(1, 10).Select(i => new site(i.ToString())); 

    static void Main(string[] args) 
    { 
     Startup().Wait(); 
     Console.ReadKey(); 
    } 

    static async Task Startup() 
    { 
     while (true) 
     { 
      router.cts = new CancellationTokenSource(); 

      await Task.WhenAll(list.Select(s => Update(s))); 
     } 
    } 

    static Task Update(site s) 
    { 
     if (site.count % 4 == 0) 
     { 
      Console.WriteLine("Reseting Queue"); 
      router.cts.Cancel(); 
     } 
     else 
     { 
      return s.Refresh(); 
     } 
    } 
} 

class router 
{ 
    public static SemaphoreSlim ss = new SemaphoreSlim(1); 
    public static CancellationTokenSource cts { get; set; } 
} 

class site 
{ 
    public static int count = 0; 
    public string sitename {get; set;} 

    public site(string s) 
    { 
     sitename = s; 
    } 

    public async Task Refresh() 
    { 
     await router.ss.WaitAsync(); 
     try 
     { 
      if (router.cts.token.IsCancellationRequested) 
      { 
       return; 
      } 
      await Task.Delay(500); 

      if (router.cts.token.IsCancellationRequested) 
      { 
       return; 
      } 
      await Task.Delay(500); 

      Console.WriteLine("{0}:: Done Refreshing ", sitename); 
      count++; 
     } 
     finally 
     { 
      router.ss.Release(); 
     } 
    } 
} 

Ho diviso il Task.Delay per renderlo più simile, a mio parere, ad un caso reale in cui si dispone di diversi tipi di operazioni (download, analizzare , salva ad esempio) e si desidera interrogare il token di cancellazione tra questi passaggi.