2009-10-14 6 views
10

J'ai un objet qui nécessite beaucoup d'initialisation (1-2 secondes sur une machine costaude). Bien qu'une fois initialisé, il ne faut que 20 millisecondes pour faire un "job" typiqueUn bon moyen d'avoir un fil de travail sans fin?

Afin d'éviter qu'il ne soit réinitialisé chaque fois qu'une application veut l'utiliser (ce qui pourrait être 50 fois par seconde ou non J'ai décidé de lui donner un travail, et de le faire fonctionner sur son propre fil, en vérifiant s'il y a du travail pour ça dans le. Cependant, je ne suis pas entièrement sûr de savoir comment faire un fil qui fonctionne indéfiniment avec ou sans travail.

Voici ce que j'ai jusqu'à présent, toute critique est accueillie

private void DoWork() 
    { 
     while (true) 
     { 
      if (JobQue.Count > 0) 
      { 
       // do work on JobQue.Dequeue() 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(50); 
      } 
     } 
    } 

Après la pensée: je pensais que je peux avoir besoin de tuer ce fil grâce INSEAD de le laisser courir pour toujours, donc je pense que je vais ajouter un Type de travail qui indique le thread à la fin. Toutes les pensées sur la façon de mettre fin à un fil comme celui-ci ont également apprécié.

Répondre

19

Vous besoin-lock de toute façon, vous pouvez Wait et Pulse:

while(true) { 
    SomeType item; 
    lock(queue) { 
     while(queue.Count == 0) { 
      Monitor.Wait(queue); // releases lock, waits for a Pulse, 
           // and re-acquires the lock 
     } 
     item = queue.Dequeue(); // we have the lock, and there's data 
    } 
    // process item **outside** of the lock 
} 

avec ajouter comme:

lock(queue) { 
    queue.Enqueue(item); 
    // if the queue was empty, the worker may be waiting - wake it up 
    if(queue.Count == 1) { Monitor.PulseAll(queue); } 
} 

Vous pouvez également regarder this question, ce qui limite la taille de la file d'attente (blocage s'il est trop plein).

+0

File d'attente classique, verrouillée et pulsée. –

+0

+1 très agréable ... –

+0

Pourquoi écrire quelque chose qui a déjà été écrit? Pfx, bébé. – Will

2

Vous avez besoin d'une primitive de synchronisation, comme WaitHandle (regardez les méthodes statiques). De cette façon, vous pouvez «signaler» le fil de travail qu'il y a du travail. Il vérifie la file d'attente et continue à travailler jusqu'à ce que la file d'attente soit vide, moment auquel il attend que le mutex le signale à nouveau.

faire l'un des éléments de travail soit une commande quitter aussi, de sorte que vous pouvez signaler le thread de travail quand il est temps de sortir le fil

1

Dans la plupart des cas, je l'ai fait tout à fait semblable à la façon dont vous » J'ai mis en place - mais pas dans la même langue. J'ai eu l'avantage de travailler avec une structure de données (en Python) qui bloquera le thread jusqu'à ce qu'un élément soit mis dans la file d'attente, annulant le besoin de l'appel de sommeil.

Si .NET fournit une classe comme celle-là, je chercherais à l'utiliser. Un blocage de thread est beaucoup mieux qu'un thread tournant sur des appels de sommeil.

Le travail que vous pouvez passer pourrait être aussi simple qu'un «nul»; Si le code reçoit une valeur nulle, il sait qu'il est temps de sortir du lot et de rentrer à la maison.

1

Saisissez Parallel Framework. Il a un BlockingCollection<T> que vous pouvez utiliser comme une file d'attente de travaux. Comment vous l'utiliseriez est:

  1. Créez le BlockingCollection < T> qui contiendra vos tâches/travaux.
  2. Créer des fils qui ont une boucle sans fin (while (true) {// obtenir l'emploi de la file)
  3. Définissez les discussions en cours
  4. Ajouter des emplois à la collecte quand ils viennent disponibles

Les threads seront bloqués jusqu'à ce qu'un élément apparaisse dans la collection. Celui qui le tournera l'obtiendra (dépend du CPU). J'utilise ça maintenant et ça marche très bien.

Il a aussi l'avantage de s'appuyer sur MS pour écrire ce bit de code particulièrement méchant où plusieurs threads accèdent à la même ressource. Et chaque fois que vous pouvez demander à quelqu'un d'écrire, vous devriez y aller. En supposant, bien sûr, qu'ils ont plus de ressources techniques/tests et une expérience combinée que vous.

+0

Voulez-vous dire le CTP marqué "Ce CTP est uniquement à des fins de test"? Je ne sais pas si c'est une bonne recommandation ... en 4.0, très bien - mais c'est bêta! –

1

Si vous n'avez pas vraiment besoin de quitter le thread (et que vous ne voulez pas que votre application continue à fonctionner), vous pouvez définir Thread.IsBackground à true et il se terminera lorsque tous les threads non-background se termineront. Will et Marc ont tous deux de bonnes solutions pour gérer la file d'attente.

1

J'ai implémenté une file d'attente de tâches en arrière-plan sans utiliser aucune sorte de boucle while, ni pulser, ou en attente, ou même, toucher Thread objets du tout. Et cela semble fonctionner. (Cela signifie que j'ai été dans des environnements de production traitant des milliers de tâches par jour pendant les 18 derniers mois sans aucun comportement inattendu.) C'est une classe avec deux propriétés significatives, une Queue<Task> et une BackgroundWorker. Il y a trois méthodes significatives, abrégées ici:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    if (TaskQueue.Count > 0) 
    { 
     TaskQueue[0].Execute(); 
    } 
} 

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    Task t = TaskQueue[0]; 

    lock (TaskQueue) 
    { 
     TaskQueue.Remove(t); 
    } 
    if (TaskQueue.Count > 0 && !BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

public void Enqueue(Task t) 
{ 
    lock (TaskQueue) 
    { 
     TaskQueue.Add(t); 
    } 
    if (!BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

Ce n'est pas qu'il n'y a pas d'attente et de pulsation. Mais tout cela se passe à l'intérieur du BackgroundWorker. Cela se réveille à chaque fois qu'une tâche est supprimée dans la file d'attente, s'exécute jusqu'à ce que la file d'attente soit vide, puis se rendormir.

Je suis loin d'être un expert en filetage. Y at-il une raison de jouer avec System.Threading pour un problème comme celui-ci si l'utilisation d'un BackgroundWorker fera l'affaire?

+0

En général, je suis d'accord que la classe BackgroundWorker est très souvent un choix plus simple pour gérer une tâche d'arrière-plan. Dans ce cas, je dois contester certaines de vos revendications.Vous utilisez des objets de threading: le mot clé 'lock' est du sucre syntaxique pour' System.Threading.Monitor.Enter() 'et' System.Threading.Monitor.Exit() '. Au lieu d'une boucle while, votre gestionnaire d'événements RunWorkerCompleted appelle 'BackgroundWorker.RunWorkerAsync()'. Je pense que la logique d'une boucle wait/pulse while pourrait être plus facile à suivre dans ce cas. –

+0

Assez juste. Cela me semble probablement plus simple parce que je ne suis pas expérimenté avec wait/pulse. –

Questions connexes