2017-09-22 9 views
1

Dans mon code actuel, j'ai une méthode comme celui-ci pour lire les données à partir d'un périphérique (pseudo code):flux de retour immédiatement puis écrire à diffuser de manière asynchrone

public async Task<string> ReadAllDataFromDevice() 
{ 
    var buffer = ""; 
    using (var device = new Device()) 
    { 
     while(device.HasMoreData) 
     { 
      buffer += await device.ReadLineAsync(); 
     } 
    } 
    return buffer; 
} 

Je veux alors d'envoyer toutes ces données via le réseau à un récepteur. La quantité de données peut être vraiment grande. Donc, clairement, la conception ci-dessus n'est pas très efficace sur le plan de la mémoire, car elle nécessite de lire toutes les données avant de pouvoir commencer à l'envoyer à la prise réseau.

Donc, ce que je voudrais avoir est une fonction qui retourne un flux à la place. Quelque chose comme ceci:

public async Task<Stream> ReadAllDataFromDevice() 
{ 
    var stream = new MemoryStream(); 
    using (var device = new Device()) 
    using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true)) 
    { 
     while(device.HasMoreData) 
     { 
      var line = await device.ReadLineAsync(); 
      await streamWriter.WriteLineAsync(line); 
     } 
     await streamWriter.FlushAsync(); 
    } 
    return stream; 
} 

Cela renvoie un flux mais il ne résout pas clairement mon problème, car le flux est retourné seulement après toutes les données ont été lues à partir du dispositif.

Alors je suis venu avec ceci:

public Stream ReadAllDataFromDevice() 
{ 
    var stream = new MemoryStream(); 
    Task.Run(async() => { 
     using (var device = new Device()) 
     using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true)) 
     { 
      while(device.HasMoreData) 
      { 
       var line = await device.ReadLineAsync(); 
       await streamWriter.WriteLineAsync(line); 
      } 
      await streamWriter.FlushAsync(); 
     } 
    }); 
    return stream; 
} 

Est-ce une bonne conception? Je suis particulièrement préoccupé par la sécurité des threads, la durée de vie de l'objet stream utilisé dans le lambda et la gestion des exceptions.

Ou existe-t-il un meilleur modèle pour ce genre de problème?

Modifier

En fait, je suis juste venu avec une autre conception qui semble me beaucoup plus propre. Au lieu d'avoir la fonction ReadAllDataFromDevice() retour d'un flux, je laisse le consommateur des données fournissent le flux, comme ceci:

public async Task ReadAllDataFromDevice(Stream stream) 
{ 
    using (var device = new Device()) 
    using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true)) 
    { 
     while(device.HasMoreData) 
     { 
      var line = await device.ReadLineAsync(); 
      await streamWriter.WriteLineAsync(line); 
     } 
     await streamWriter.FlushAsync(); 
    } 
} 
+0

Comment savez-vous quand il n'y a plus de données à recevoir? Cette ligne n'a aucun sens: device.HasMoreData. – jdweng

Répondre

0

C'est la conception que je utilise maintenant:

public async Task ReadAllDataFromDevice(Func<Stream, Task> readCallback) 
{ 
    using (var device = new Device()) 
    { 
     await device.Initialize(); 
     using (var stream = new DeviceStream(device)) 
     { 
      await readCallback(stream); 
     } 
    } 
} 

La ligne L'accès par périphérique par ligne est encapsulé dans la classe personnalisée DeviceStream (non représentée ici).

Le consommateur des données ressemblerait à quelque chose comme ceci:

await ReadAllDataFromDevice(async stream => { 
    using (var streamReader(stream)) 
    { 
     var data = await streamReader.ReadToEndAsync(); 
     // do something with data 
    } 
});