2010-05-06 4 views
0

Actuellement, j'ai une classe téléchargeur multi-thread qui utilise HttpWebRequest/Response. Tout fonctionne bien, c'est super rapide, MAIS .. le problème est que les données doivent être diffusées pendant le téléchargement vers une autre application. Cela signifie qu'il doit être diffusé dans le bon ordre, le premier morceau en premier, puis le suivant dans la file d'attente. Actuellement, ma classe de téléchargement est synchronisée et Download() renvoie octet []. Dans ma classe multi-threadée asynchrone je fais par exemple, liste avec 4 éléments vides (pour les slots) et je passe chaque index du slot à chaque thread en utilisant la fonction Download(). Cela simule la synchronisation, mais ce n'est pas ce dont j'ai besoin. Comment dois-je faire la chose de file d'attente, pour s'assurer que les données sont diffusées dès que le premier morceau commence à télécharger.Téléchargeur multi-thread en C# question

Répondre

2

Si votre question porte sur la façon de déterminer quel thread télécharge le premier morceau et quand ce premier morceau est prêt à être utilisé, utilisez un événement par fil et garder une trace de quels morceaux vous avez assigné à quels fils. Gardez une trace de l'événement que vous passez au premier thread (qui va télécharger le premier morceau de données), l'événement que vous passez au deuxième thread (pour le deuxième morceau de données), etc. Le thread principal, ou un autre thread d'arrière-plan (pour éviter de bloquer le thread d'interface utilisateur), attendez le premier événement. Lorsque le premier thread finit de télécharger son bloc, il définit/signale le premier événement. Le thread en attente se réveille et peut utiliser le premier bloc de données.

Les autres threads de téléchargement peuvent faire de même, signalant leurs événements respectifs lorsqu'ils sont terminés. Utilisez un événement de réinitialisation manuelle pour que l'événement reste signalé même si personne ne l'attend. Lorsque le thread qui a besoin des blocs de données dans l'ordre termine le traitement du premier bloc de données, il peut attendre le 2ème événement. Si le deuxième événement a déjà été signalé, l'attente revient immédiatement et le thread peut commencer à traiter le deuxième bloc de données. Pour un très gros téléchargement, vous pouvez réutiliser les événements et les discussions de manière circulaire. L'ordre qu'ils finissent n'est pas important tant que le thread qui consomme les morceaux de données les consomme dans l'ordre et attend les événements respectifs dans l'ordre.Si vous êtes intelligent et prudent, vous pouvez probablement faire tout cela en utilisant un seul événement: créer un tableau global de pointeurs de blocs de données/objets initialement définis sur null, threads de travail télécharger des blocs de données et affecter les morceaux finis à leur emplacement respectif dans le tableau global, puis signaler l'événement partagé. Le thread consommateur conserve un compteur de blocs de données pour qu'il sache quel bloc de données il doit gérer, attend l'événement partagé et, lorsqu'il est signalé, il regarde l'emplacement suivant dans le tableau global pour voir si des données y sont apparues. S'il n'y a toujours pas de données dans l'emplacement suivant dans la séquence, le thread consommateur revient à l'attente de l'événement. Vous aurez également besoin d'un moyen pour que les threads de travail sachent quel bloc de données ils doivent télécharger ensuite - un compteur global protégé par un mutex ou accédé en utilisant interlockedadd/exchange suffira. Chaque thread de travail incrémente le compteur global et télécharge ce numéro de bloc de données et affecte le résultat au nième emplacement de la liste globale des blocs de données.

+0

Eh bien, c'est mon code: http: // pastie.org/949929 DownloadSingleChunk (index, startPos, endPos) et tandis qu'il obtient la plage spécifiée, il l'écrit dans la liste chunkOutput . J'ai pensé le chemin en utilisant le compteur, mais cela signifie que je devrais boucler mon thread principal, ou un autre fil de travail. Est-ce correct? – blez

+0

Si votre téléchargement est supérieur à quelques mégaoctets, oui, vous devriez avoir chaque thread de travail en boucle pour ramasser plus de blocs à télécharger. Vous voulez garder votre nombre de threads assez petit et garder la taille de chaque morceau de téléchargement de devenir trop grand. Le téléchargement de blocs de données très volumineux entraîne un risque plus élevé de perte de paquets ou d'erreur de transmission, ce qui peut vous obliger à redémarrer tout le bloc. Les petits blocs qui peuvent transmettre dans une minute ou deux chacun sont plus résilients aux erreurs de réseau, IMO. – dthorpe

+0

Et oui, vous devriez probablement avoir un autre thread à boucler et attendre que le prochain bloc de données dans le tableau devienne disponible et l'envoyer ensuite à l'endroit où vous l'envoyez, décrit comme le "thread de consommation" à la fin de mon poster. Vous ne voulez pas que votre thread d'interface utilisateur principal bloque l'attente d'un événement, car cela entraînera le blocage de votre interface utilisateur. – dthorpe

0

Pour créer un téléchargeur multithread synchronisé, vous devez créer une structure de données correcte, et vous aurez besoin de plus que simplement byte[] de données.

Étapes:

  1. Pause téléchargement en plusieurs morceaux en fonction de la taille du contenu ou téléchargeur de taille fixe contenu 500KB téléchargé par chaque fil.
  2. Lors du démarrage du thread, spécifiez l'index de segment - 1ère partie, 2ème partie, etc.
  3. Lorsque le téléchargement est disponible, alignez le contenu final en fonction de l'index de tronçon.

Si vous êtes intéressé, vous pouvez regarder le code de prozilla (C, basé sur Linux - at) ou Axel.

+0

Je l'ai déjà fait. J'ai 5 morceaux (threads), 1mb chacun. Mais je ne produis les données que si les 5 threads sont terminés, car je ne sais pas comment vérifier l'ordre. – blez

+0

Pas besoin d'attendre que les 5 threads soient terminés. Vous pouvez avoir encore une autre stratégie - créer un fichier de 5 Mo de taille et au fur et à mesure que les réponses de thread sont disponibles, vider les sur le fichier! :) –

+0

Oui, mais j'ai besoin de diffuser les données, pas particulièrement utiliser le fichier. C'est pourquoi je ne peux pas utiliser cette méthode. – blez

0

Pouvez-vous montrer le code dans lequel vous effectuez les téléchargements, et le code où vous démarrez les multiples threads asynchrones? Peut-être que je ne comprends pas complètement votre scénario, mais si j'étais vous, j'utiliserais Async (BeginRead sur le responseStream). Ensuite, je ferais ce qui suit ....

void StartReading(Stream responseStream) 
{ 
    byte [] buffer = new byte[1024]; 
    Context ctx = new Context(); 
    ctx.Buffer = buffer; 
    ctx.InputStream = responseStream; 
    ctx.OutputStream = new MemoryStream(); // change this to be your output stream 

    responseStream.BeginRead(buffer, 0, buffer.Length; new AsyncCallback(ReadCallback), ctx); 
} 

void ReadCallback(IAsyncResult ar) 
{ 
    Context ctx = (Context)ar.AsyncState; 
    int read = 0; 
    try { 
     read = ctx.InputStream.EndRead(ar); 
     if (read > 0) 
     { 
      ctx.OutputStream.Write(ctx.Buffer, 0, read); 
      // kick off another async read 
      ctx.InputStream.BeginRead(ctx.Buffer, 0, ctx.Buffer.Length, new AsyncCallback(ReadCallback), ctx); 
     } else { 
      ctx.InputStream.Close(); 
      ctx.OutputStream.Close(); 
     } 
    } catch { 
    } 
} 

}