2015-07-17 16 views
5

Vedere original Microsoft example.Console.ReadKey() non funzionante

Nel secondo esempio troviamo il seguente codice:

// The simplest UI thread ever invented. 
Task.Run(() => 
{ 
    if (Console.ReadKey().KeyChar == 'c') 
     cts.Cancel(); 
}); 

(codice completo necessario per eseguire questo :)

static int inputs = 2000; 
static void Main(string[] args) 
{ 
    // The token source for issuing the cancelation request. 
    CancellationTokenSource cts = new CancellationTokenSource(); 

    // A blocking collection that can hold no more than 100 items at a time. 
    BlockingCollection<int> numberCollection = new BlockingCollection<int>(100); 

    // Set console buffer to hold our prodigious output. 
    //Console.SetBufferSize(80, 2000); 

    // The simplest UI thread ever invented. 
    Task.Run(() => 
    { 
     if (Console.ReadKey().KeyChar == 'c') 
      cts.Cancel(); 
     else 
     { 
      Debugger.Break(); 
     } 
    }); 

    // Start one producer and one consumer. 
    Task.Run(() => NonBlockingConsumer(numberCollection, cts.Token)); 
    Task.Run(() => NonBlockingProducer(numberCollection, cts.Token)); 

    Console.WriteLine("Press the Enter key to exit."); 
    Console.ReadLine(); 
} 

static void NonBlockingConsumer(BlockingCollection<int> bc, CancellationToken ct) 
{ 
    // IsCompleted == (IsAddingCompleted && Count == 0) 
    while (!bc.IsCompleted) 
    { 
     int nextItem = 0; 
     try 
     { 
      if (!bc.TryTake(out nextItem, 0, ct)) 
      { 
       Console.WriteLine(" Take Blocked"); 
      } 
      else 
       Console.WriteLine(" Take:{0}", nextItem); 
     } 

     catch (OperationCanceledException) 
     { 
      Console.WriteLine("Taking canceled."); 
      break; 
     } 

     // Slow down consumer just a little to cause 
     // collection to fill up faster, and lead to "AddBlocked" 
     Thread.SpinWait(500000); 
    } 

    Console.WriteLine("\r\nNo more items to take. Press the Enter key to exit."); 
} 

static void NonBlockingProducer(BlockingCollection<int> bc, CancellationToken ct) 
{ 
    int itemToAdd = 0; 
    bool success = false; 

    do 
    { 
     // Cancellation causes OCE. We know how to handle it. 
     try 
     { 
      // A shorter timeout causes more failures. 
      success = bc.TryAdd(itemToAdd, 2, ct); 
     } 
     catch (OperationCanceledException) 
     { 
      Console.WriteLine("Add loop canceled."); 
      // Let other threads know we're done in case 
      // they aren't monitoring the cancellation token. 
      bc.CompleteAdding(); 
      break; 
     } 

     if (success) 
     { 
      Console.WriteLine(" Add:{0}", itemToAdd); 
      itemToAdd++; 
     } 
     else 
     { 
      Console.Write(" AddBlocked:{0} Count = {1}", itemToAdd.ToString(), bc.Count); 
      // Don't increment nextItem. Try again on next iteration. 

      //Do something else useful instead. 
      UpdateProgress(itemToAdd); 
     } 

    } while (itemToAdd < inputs); 

    // No lock required here because only one producer. 
    bc.CompleteAdding(); 
} 

static void UpdateProgress(int i) 
{ 
    double percent = ((double)i/inputs) * 100; 
    Console.WriteLine("Percent complete: {0}", percent); 
} 

cosa che il codice dovrebbe fare è del tutto chiara: dovrebbe rompere su premendo c, ma non funziona. Invece funziona fino alla fine chiedendo di chiudere con Enter.

Come possiamo risolvere questo problema?

Sembra essere un problema di threading, ma è una demo per .net4.5 e il codice non funziona.

KeyPress di "c" non imposta il CancelationToken.

+8

In che modo esso non funziona? Hai effettuato il debug? – hellogoodnight

+1

Hai premuto qualche altro tasto prima di "c"? –

+1

@Fabrizio no, non tocco la tastiera prima. – GreenEyedAndy

risposta

4

Hai perfettamente ragione nell'esempio che è sbagliato. È solo rotto.

Il motivo per cui non funziona è che non attende il completamento di Task s, invece attende il Console.ReadLine. Questo lo risolverà:

Sostituire:

Task.Run(() => NonBlockingConsumer(numberCollection, cts.Token)); 
Task.Run(() => NonBlockingProducer(numberCollection, cts.Token)); 

Con:

Task t1 = Task.Run(() => NonBlockingConsumer(numberCollection, cts.Token)); 
Task t2 = Task.Run(() => NonBlockingProducer(numberCollection, cts.Token)); 

Task.WaitAll(t1, t2); 
+2

Questo è strano. Ho provato esattamente questo, ma non funziona. di nuovo, e funziona. Grazie per quello. – GreenEyedAndy

+1

Contento che ti abbia aiutato. Ho inviato un feedback sul sito MSDN degli Stati Uniti per indirizzarli a questa domanda in modo che possano risolverlo. –

+0

E l'errore è che il 'ReadLine ('' sta bloccando 'ReadKey()' nel Thread? – GreenEyedAndy