2016-11-30 3 views
0

J'ai le délégué d'événement suivant qui prend essentiellement un tableau d'octets, et l'ajoute à un flux de fichiers. Une fois qu'il a écrit 1000 fois, qui est suivi par une variable locale counter, je veux fermer le flux de fichiers en cours et en ouvrir un nouveau.Comment peut-on s'assurer que FileStream est éliminé après que toutes WriteAsync() sur le fichier en cours sont terminées?

J'ai lu la documentation de FileStream et suggéré d'utiliser FileStream.WriteAsync() pour de meilleures performances.

public void WriteData(byte[] data) 
{ 
    counter++; 
    if (counter == 1000) 
    { 
     counter = 0; 

     // Close the current filestream and open a new one. 
      filestream.Dispose(); 
      filestream = new FileStream(this.outputPath, FileMode.Create, 
      FileAccess.Write, FileShare.None, 4096, true); 
    } 

    filestream.WriteAsync(data, 0, data.Length); 
} 

Cependant, dans la fonction ci-dessus, mon hypothèse est que ce pourrait être le cas que tous les appels WriteAsync() ont pas terminé avant d'appeler filestream.Dispose(). Y a-t-il un moyen de m'assurer que je n'ai que Dispose() après que tous mes appels WriteAsync() aient été terminés? Notez que ce délégué d'événement est appelé de 1 000 à 2 000 fois par seconde et WriteAsync copie 240 Ko par appel sur un disque SSD.

Une solution que je peux penser est au lieu immédiatement la disposition de chaque Filestream immédiatement, je peux stocker dans un tableau de Filestream puis disposer une fois que je suis fini avec l'ensemble du processus d'écriture de données et plus d'événements sont cuisson. Cela fonctionnerait-il? Même alors, comment puis-je effectivement attendre que tous les appels WriteAsync() soient terminés?

+1

'await filestream.WriteAsync (...); filestream.Dispose; '? – Kritner

+0

@Kritner Le flux n'est pas éliminé après chaque écriture. – Servy

+0

Vous ne devriez pas avoir une méthode asynchrone 'void' comme ceci. Vous devriez toujours exposer à l'appelant un moyen de déterminer quand l'action asynchrone est terminée (dans ce cas, en retournant une 'tâche'). Notez qu'avec votre code actuel, non seulement vous pouvez vous débarrasser du flux pendant qu'une écriture est en cours, mais vous pouvez également envoyer une écriture supplémentaire alors qu'une écriture antérieure n'est pas encore terminée, ce qui * posera également des problèmes. – Servy

Répondre

0

Sans une réponse à la question exacte, basée sur le suivi dans les commentaires, je vous suggère de faire ce qui suit:

  • Créer une ConcurrentQueue dans tout ce qui est mise à feu des événements
  • Sur chaque événement, ajouter à cette file d'attente
  • Créez un thread distinct qui gère la file d'attente, supprime les événements et les écrit sur le disque. Vous pouvez ensuite contrôler exactement la fréquence à laquelle vous voulez ouvrir, écrire, renommer, etc., et vous n'avez aucun problème de concurrence de threads sur l'activité du fichier à traiter. En fonction de vos besoins, vous pouvez, dans votre thread d'écriture, interroger la file d'attente toutes les secondes ou utiliser une approche plus sophistiquée en utilisant WaitHandle s pour signaler que les données sont prêtes à être écrites, lorsque la file est vide, il contient N éléments, etc. Comme vous accumulez des données si souvent, peut-être que l'interrogation est bonne car vous trouverez presque toujours des données à écrire.
  • Vous devez évidemment gérer la fermeture de l'application proprement - par ex. arrêtez d'écrire à partir d'événements, sélectionnez le thread d'écriture pour arrêter, faites-le vider les derniers éléments sur le disque, attendez qu'il s'arrête.

Il est plus de travail, mais vous obtenez:

  • pour garantir
  • presque pas de temps passé dans le gestionnaire d'événements
  • aucun problème de concurrence (à l'exception de la gestion de l'installation et le démontage)
  • délais d'écriture - par exempleen raison de OS, disque, réseau - ne pas bloquer l'application principale
  • un risque de perdre des données d'événement si vous perdez l'alimentation ou l'application abandonne (vous aurez ceci avec toute solution qui n'attend pas l'écriture des données après chaque événement)

Il existe probablement des solutions «out-the-box» pour cela (en utilisant un log4net Appender tamponné); C'est si vous voulez rouler le vôtre.

Vous pouvez le faire avec « standard » de filetage (à savoir Thread), ou vous pouvez simplement créer votre fil d'écriture comme Task.