2017-07-08 6 views
0

Je voudrais lire un octet (ou caractère) de stdin (entrée standard) de processus mono C# de la manière suivante:non-bloquante d'un octet de stdin

  • S'il y a déjà des octets disponibles pour la lecture de stdin, retourner le premier
  • S'il n'y a pas d'octets disponibles pour la lecture de stdin, procéder sans attendre quoi que ce soit ou de bloquer
  • S'il y a des fils ou async, il devrait être succint et prouvable correcte solution goutte dans d'obtenir un seul octet sur demande
  • stdin peut b Le tuyau ou le fichier ne fera pas Console.KeyAvailable

Merci.

+0

Il n'y a pas de manière indépendante de la plate-forme de faire async stdin I/O, ce qui explique pourquoi la BCL n'essaie pas. Les opérations de lecture sur le flux d'entrée standard s'exécutent de manière synchrone, c'est-à-dire qu'elles se bloquent jusqu'à ce que l'opération de lecture spécifiée soit terminée. " Peut-il être mis en œuvre sur Mono? Probablement, si vous aimez P/Invoke et '# if 'pour prendre soin des différentes plates-formes. Il ne vaut certainement pas la peine de le faire, par opposition à la rotation d'un thread pour envelopper l'opération de synchronisation (et en utilisant 'Console.IsInputRedirected' pour distinguer ce cas). –

+0

@JeroenMostert écrire plus de code est exactement ce que j'essaie d'éviter. Je vais donner un exemple vraiment succint de la façon dont cela peut être fait le plus rapidement possible. L'idée est d'éviter de dépenser une quantité non négligeable de code ou d'effort sur le problème. Allons, c'est 2017! Et nous ne pouvons pas lire un octet d'un flux sans threads ou blocage! – alamar

+0

Il s'agit en effet de 2017, mais les E/S de console non bloquantes et non bloquantes ne sont tout simplement pas une fonctionnalité très demandée. La mode a toujours été des applications console qui traitent sereinement leur entrée d'une manière synchrone, monothread. Si vous êtes satisfait de l'interrogation plutôt que de la vraie async (comme vous l'êtes), il est assez facile à faire sur Unix et Win32 (bien que non, bien sûr, de la même manière, ce serait trop facile), mais .NET le plus petit dénominateur commun, ce qui n'aide pas. Je ne pouvais pas trouver une solution triviale dans 10 minutes. Ne pas dire qu'il n'y en a pas *. –

Répondre

2

fourni votre plate-forme a une implémentation de System.Collections.Concurrent.BlockingCollection, voici facile si façon pas tout à fait efficace de le faire:

class ConsoleReader { 
    private readonly BlockingCollection<int> buffer = new BlockingCollection<int>(1); 
    private readonly Thread readThread; 
    public ConsoleReader() { 
    readThread = new Thread(() => { 
     if (Console.IsInputRedirected) { 
     int i; 
     do { 
      i = Console.Read(); 
      buffer.Add(i); 
     } while (i != -1); 
     } else { 
     while (true) { 
      var consoleKeyInfo = Console.ReadKey(true); 
      if (consoleKeyInfo.KeyChar == 0) continue; // ignore dead keys 
      buffer.Add(consoleKeyInfo.KeyChar); 
     } 
     } 
    }); 
    } 

    public void Start() { 
    readThread.Start(); 
    } 

    public int? Next { 
    get { 
     int result; 
     return buffer.TryTake(out result, 0) ? result : default(int?); 
    } 
    } 
} 

Avertissement: cette réponse n'a pas été réellement testé sur Mono, et brièvement sur la pleine Plate-forme .NET. Vous pouvez pimenter ceci avec un Task au lieu d'un Thread, une annulation correcte etc., mais cela ne changera pas l'inefficacité centrale d'attendre explicitement sur une opération synchrone (qui Console.Read et Console.ReadKey sont tous les deux). Les E/S de console asynchrones portables ne sont pas proposées par .NET lui-même. L'implémenter vous-même sur toutes les plateformes (en utilisant les fonctionnalités du système d'exploitation) est possible, mais décidément non trivial.

+0

En fait, il ya une console asynchrone portable E/S sous la forme de Console.KeyAvailable. C'est le scénario de redirection d'entrée qui me pose problème - lecture non bloquante du flux de redirection d'entrée (== fichier). – alamar

+0

@alamar: 'Console.KeyAvailable' est en effet * non-bloquant *, ce qui n'est pas la même chose que * asynchrone *, qui a une signification plutôt distincte dans .NET - il n'y a pas de' Console.BeginReadKey() 'ou 'Console.ReadKeyAsync()'. Il y a *, cependant, 'Stream.BeginRead()', et 'Console.OpenStandardInput()'. Le flux de la console est toujours synchrone, mais '.BeginRead()' fournira toujours un wrapper asynchrone-sur-synchronisation. Cela peut réaliser ce que vous voulez. –

+0

@alamar: notez que lorsque vous lisez depuis la console, ni Console.Read() 'ni le flux d'entrée ne retournent quoi que ce soit tant qu'une ligne entière n'a pas été lue, même si vous ne demandez qu'un seul caractère. C'est pourquoi '.OpenStandardInput()' /'.BeginRead() 'n'est pas une solution universelle. Mais vous pouvez combiner cela avec '.IsInputRedirected'. –