Question

J'ai une application qui a de nombreux cas. Chaque cas a de nombreux fichiers multipages TIF. Je dois covert les fichiers tf fichier pdf. Comme il y a tellement de fichier, je pensais que je pourrais enfiler le processus de conversion. Je limite actuellement le processus à dix conversions à la fois (i.e. dix marches). Quand on la conversion est terminée, une autre doit commencer.

Ceci est la configuration actuelle j'utilise.

private void ConvertFiles()
{
  List<AutoResetEvent> semaphores = new List<AutoResetEvet>();
  foreach(String fileName in filesToConvert)
  {
    String file = fileName;

    if(semaphores.Count >= 10)
    {
      WaitHandle.WaitAny(semaphores.ToArray());
    }


    AutoResetEvent semaphore = new AutoResetEvent(false);
    semaphores.Add(semaphore);

    ThreadPool.QueueUserWorkItem(
      delegate
      { 
        Convert(file);
        semaphore.Set();
        semaphores.Remove(semaphore);
      }, null);
  }

  if(semaphores.Count > 0)
  {
    WaitHandle.WaitAll(semaphores.ToArray());
  }
}

L'utilisation de ce, aboutit parfois à une exception indiquant les paramètres du tableau WaitHandle.WaitAll () ou WaitHandle.WaitAny () ne doit pas dépasser une longueur de 65 Qu'est-ce que je fais mal dans cette approche et comment puis-je corriger?

Était-ce utile?

La solution

Il y a quelques problèmes avec ce que vous avez écrit.

1er, il est pas thread-safe. Vous avez plusieurs threads ajout, la suppression et d'attente sur le tableau de AutoResetEvents. Les différents éléments de la liste sont accessibles sur des threads séparés, mais tout ce qui ajoute, supprime ou vérifie tous les éléments (comme l'appel WaitAny), doivent le faire à l'intérieur d'une serrure.

2, il n'y a aucune garantie que votre code ne traitera 10 fichiers à la fois. Le code entre le moment où la taille de la liste est cochée, et le point où un nouvel élément est ajouté est ouvert pour plusieurs threads pour passer à travers.

3, il est possible que les discussions ont commencé dans la QueueUserWorkItem pour convertir le même fichier. Sans la capture fileName dans la boucle, le fil qui convertit le fichier utilisera toute valeur dans fileName quand il exécute, pas tout ce qui était en fileName quand vous avez appelé QueueUserWorkItem.

Cet article CodeProject devrait vous indiquer la bonne direction pour ce que vous essayez de faire: http://www.codeproject.com/KB/threads/SchedulingEngine.aspx

EDIT:

var semaphores = new List<AutoResetEvent>();
        foreach (String fileName in filesToConvert)
        {
            String file = fileName;
            AutoResetEvent[] array;
            lock (semaphores)
            {
                array = semaphores.ToArray();
            }
            if (array.Count() >= 10)
            {
                WaitHandle.WaitAny(array);
            }

            var semaphore = new AutoResetEvent(false);
            lock (semaphores)
            {
                semaphores.Add(semaphore);
            }
            ThreadPool.QueueUserWorkItem(
              delegate
              {
                  Convert(file);
                  lock (semaphores)
                  {
                      semaphores.Remove(semaphore);
                  }
                  semaphore.Set();
              }, null);
        }

Personnellement, je ne pense pas que je le ferais de cette façon ... mais, en travaillant avec le code que vous avez, cela devrait fonctionner.

Autres conseils

Utilisez-vous un vrai sémaphores ( System.Threading )? Lorsque vous utilisez sémaphores, vous allouez généralement vos ressources max et il va bloquer automatiquement pour vous (comme vous ajoutez et libération). Vous pouvez aller avec l'approche WaitAny, mais je reçois le sentiment que vous avez choisi la route la plus difficile.

On dirait que vous devez retirer la poignée du déclenchement de la fonction WaitAny de procéder

if(semaphores.Count >= 10)
{
  int index = WaitHandle.WaitAny(semaphores.ToArray());
  semaphores.RemoveAt(index);
}

Donc, fondamentalement, je supprimerais le:

semaphores.Remove(semaphore);

appel du fil et utiliser ce qui précède pour supprimer l'événement signalé et voir si cela fonctionne.

Peut-être que vous ne devriez pas créer tant d'événements?

// input
var filesToConvert = new List<string>();
Action<string> Convert = Console.WriteLine;

// limit
const int MaxThreadsCount = 10;

var fileConverted = new AutoResetEvent(false);
long threadsCount = 0;

// start
foreach (var file in filesToConvert) {
    if (threadsCount++ > MaxThreadsCount) // reached max threads count 
        fileConverted.WaitOne();          // wait for one of started threads

    Interlocked.Increment(ref threadsCount);

    ThreadPool.QueueUserWorkItem(
        delegate {
            Convert(file);

            Interlocked.Decrement(ref threadsCount);
            fileConverted.Set();
        });
}

// wait
while (Interlocked.Read(ref threadsCount) > 0) // paranoia?
    fileConverted.WaitOne();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top