web-dev-qa-db-fra.com

Est-il sûr d'utiliser un indicateur booléen pour empêcher un thread de s'exécuter en C #

Ma principale préoccupation concerne l'indicateur booléen ... est-il sûr de l'utiliser sans aucune synchronisation? J'ai lu à plusieurs endroits que c'est atomique (y compris la documentation).

class MyTask
{
    private ManualResetEvent startSignal;
    private CountDownLatch latch;
    private bool running;

    MyTask(CountDownLatch latch)
    {
        running = false;
        this.latch = latch;
        startSignal = new ManualResetEvent(false);
    }

    // A method which runs in a thread
    public void Run()
    {
        startSignal.WaitOne();
        while(running)
        {
            startSignal.WaitOne();
            //... some code
        }
        latch.Signal();
    }

    public void Stop()
    {
        running = false;
        startSignal.Set();
    }

    public void Start()
    {
        running = true;
        startSignal.Set();
    }

    public void Pause()
    {
        startSignal.Reset();
    }

    public void Resume()
    {
        startSignal.Set();
    }
}

Est-ce un moyen sûr de concevoir une tâche de cette manière? Des suggestions, des améliorations, des commentaires?

Remarque: j'ai écrit ma classe CountDownLatch personnalisée au cas où vous vous demanderiez d'où je l'obtiens.

Mise à jour:
Voici aussi mon CountDownLatch:

public class CountDownLatch 
{
    private volatile int m_remain;
    private EventWaitHandle m_event;

    public CountDownLatch (int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}
35
Kiril

Vous feriez mieux de le marquer volatile cependant:

Le mot clé volatile indique qu'un champ peut être modifié par plusieurs threads s'exécutant simultanément. Les champs déclarés volatils ne sont pas soumis aux optimisations du compilateur qui supposent l'accès par un seul thread. Cela garantit que la valeur la plus à jour est présente dans le champ à tout moment.

Mais je changerais votre boucle:

    startSignal.WaitOne();
    while(running)
    {
        //... some code
        startSignal.WaitOne();
    }

Tel qu'il est dans votre message, le 'certain code' peut s'exécuter lorsque le thread est arrêté (c'est-à-dire lorsque Stop est appelé), ce qui est inattendu et peut être même incorrect.

48
Remus Rusanu

Les booléens sont atomiques en C #, cependant, si vous voulez le modifier dans un thread et le lire dans un autre, vous devrez au moins le marquer comme volatile. Sinon, le thread de lecture ne peut le lire qu'une seule fois dans un registre.

5
Michael Burr
2
Kevin Sylvestre

BTW, je viens de remarquer cette partie du code:

// A method which runs in a thread
    public void Run()
    {
        startSignal.WaitOne();
        while(running)
        {
            startSignal.WaitOne();
            //... some code
        }
        latch.Signal();
    }

Vous devrez débloquer le thread de travail deux fois en utilisant "startSignal.Set ()" pour que le code du bloc while s'exécute.

Est-ce délibéré?

0
akapet