6 votes

Réveiller un fil de discussion lorsqu'un événement se produit

Je voudrais réaliser une communication suivante entre deux fils :

Le fil Alpha fait quelque chose, puis se suspend. Ensuite, le deuxième fil (Beta) lève un événement qui reprend le fil Alpha. Le cycle continue...

J'ai fait quelque chose comme ci-dessous mais je ne suis pas sûr que ce soit une conception correcte. J'ai également remarqué que Thread.Suspend() y Thread.Resume() sont dépréciés. Je suis impatient d'entendre tout avis sur cette mise en œuvre et sur ce qui est préférable pour remplacer les méthodes dépréciées.

namespace ThreadTester
{
    delegate void ActionHandler();

    class Alpha
    {
        internal Thread alphaThread;
        internal void Start()
        {
            while (true)
            {
                this.alphaThread.Suspend();
                Console.WriteLine("Alpha");
            }
        }
        internal void Resume()
        {
            while (this.alphaThread.ThreadState == ThreadState.Suspended)
            this.alphaThread.Resume();
        }
    }

    class Beta
    {
        internal event ActionHandler OnEvent;
        internal void Start()
        {
            for (int i = 0; i < 15; i++)
            {
                OnEvent();
                Thread.Sleep(1000);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Alpha alpha = new Alpha();
            alpha.alphaThread = new Thread(new ThreadStart(alpha.Start));
            alpha.alphaThread.Start();
            while (!alpha.alphaThread.IsAlive) ;

            Beta beta = new Beta();
            beta.OnEvent += new ActionHandler(alpha.Resume);
            Thread betaThread = new Thread(new ThreadStart(beta.Start));
            betaThread.Start();
        }
    }
}

14voto

Joren Points 7911

C'est ici que vous utilisez généralement les poignées d'attente en particulier les poignées d'attente d'événements.

Si vous appelez le WaitOne sur un handle d'attente, il bloquera votre thread jusqu'à ce qu'un autre thread appelle Set sur la même poignée d'attente.

Il existe deux types importants et simples de poignées d'attente d'événements : AutoResetEvent se réinitialisera automatiquement après le passage d'un fil de discussion. WaitOne . ManualResetEvent ne se réinitialisera que si vous appelez Reset .

10voto

Dan Bryant Points 19021

Il s'agit d'un problème de synchronisation courant et il existe plusieurs approches (qui sont toutes assez faciles à gâcher, si vous n'êtes pas extrêmement prudent) :

  1. Poignées d'attente (Joren les a déjà décrites).

  2. Monitor.Wait y Moniteur.pouls . Le problème est que l'impulsion ne réveille que les threads qui sont déjà dans l'état d'attente. Il faut donc faire attention à la façon dont on gère le verrouillage de l'objet de synchronisation.

  3. Avec le nouveau TPL Dans .NET 4 (également porté en arrière vers .NET 3.5), vous pouvez configurer des tâches asynchrones et définir des conditions dans lesquelles les tâches se poursuivent en fonction des tâches précédemment terminées. Il y a un peu de courbe d'apprentissage pour comprendre comment structurer votre code afin de tirer parti des tâches et de la continuation, mais c'est considérablement mieux (à long terme) que le chemin très rocailleux qui suit l'utilisation faussement simple des constructions de synchronisation de niveau inférieur. Cela vous donne également une excellente voie à suivre pour ajouter une gestion d'erreur plus robuste et un support d'annulation à votre logique, puisque les détails de la coordination de ces éléments sont intégrés dans la TPL.

7voto

Brian Gideon Points 26683

Votre code a la "sensation" du modèle producteur-consommateur, mais il est implémenté de la mauvaise façon. Vous ne voulez absolument pas utiliser Thread.Suspend y Thread.Resume de cette manière (ou de toute autre manière en fait). Il est en fait facile d'obtenir le séquençage et la signalisation comme vous le souhaitez en implémentant le modèle canonique producteur-consommateur via la fonction BlockingCollection classe.

public class ProducerConsumer
{
    private BlockingCollection<object> m_Queue = new BlockingCollection<object>();

    public ProducerConsumer()
    {
        new Thread(Producer).Start();
        new Thread(Consumer).Start();
    }

    private void Consumer()
    {
        while (true)
        {
            object item = m_Queue.Take(); // blocks when the queue is empty
            Console.WriteLine("Consumer");
        }
    }

    private void Producer()
    {
        while (true)
        {
            m_Queue.Add(new object());
            Thread.Sleep(1000);
        }
    }
}

Dans le code ci-dessus Consumer y Producer serait équivalent à votre alphaThread y betaThread respectivement.

2voto

Steve Townsend Points 36948

En général, les threads sont utilisés pour permettre à > 1 élément de travail d'être traité en parallèle. Je suis curieux de savoir pourquoi votre conception nécessite que le thread A dorme pendant que le thread B fait quelque chose, puis se réveille et continue à travailler. Pourquoi ne pas laisser le fil A faire ce travail lui-même ?

Vous pourriez tirer profit de l'utilisation de l'interface de .Net 4 Bibliothèque parallèle des tâches - Le thread A peut alors lancer une tâche asynchrone qui est automatiquement exécutée sur un thread séparé, et le résultat est mis à la disposition du thread A sans qu'il soit nécessaire d'établir une signalisation inter-thread explicite (ce qui peut entraîner un blocage de l'application en cas de dysfonctionnement du thread A ou B).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X