Suspendre le fil d'un autre fil (s) et également arrêter/démarrer encore d'un autre fil

J'ai déjà trouvé de bons conseils ici . Mais j'ai une tâche beaucoup plus difficile - les demandes supplémentaires sont:
- mon thread de priorité basse pour toujours peut être démarré/arrêté depuis le thread principal (c'est pareil) - mais il doit également verrouiller une ressource pour un accès exclusif.
- mon thread de priorité basse pour toujours peut être mis en pause/continué à partir d'un autre thread (s) de haute priorité (ils verrouillent et utilisent ensuite cette ressource)
- aussi je veux que le thread de basse priorité ne débloque pas le verrou dans chaque boucle, mais relâche seulement le verrou une fois qu'on le lui demande (à des fins de vitesse - car je devrais init/deinit resource) verrouiller chaque boucle alors je peux laisser java gérer des threads concurrents et s'attendre à ce que les threads prioritaires gagnent de temps en temps).

Je propose une solution et je pense que c'est sain et sauf. Mais je suis novice, donc je suis ouvert pour trouver un bug dans ma solution ou des suggestions d'améliorations. Donc mes questions sont:
1) Ma solution est-elle vraiment sûre dans tous les cas?
2) Est-ce une solution optimale ou puis-je améliorer quelque chose?

Si c'est votre accord, laissez-le servir de modèle.

Voici le code de base:

FlagNotify lowPriorRunReq   = new FlagNotify(false);//low priority task run request
FlagNotify lowPriorPauseReq = new FlagNotify(false);//low priority task pause request (it uses notify)
volatile boolean lowPriorRunFlag = false; //low priority task run flag
Lock      groupLock        = new ReentrantLock(); //group lock (used to acquire lowPriorRunFlag always correctly)
Semaphore resourceSemaphore = new Semaphore(1);   //main semaphore protecting resource that has to be accessed sequentially

public class PrioritySingleTaskThread extends Thread {
    @Override
    public void run() {
        prn("High Priority Task created");
        groupLock.lock();
        if(lowPriorRunFlag == true) lowPriorPauseReq.setNotify(true);
        resourceSemaphore.acquireUninterruptibly();
        groupLock.unlock();
        accessResource("high priority task");
        resourceSemaphore.release();
        groupLock.lock();
        if(lowPriorPauseReq.get() == true) lowPriorPauseReq.setNotify(false);
        groupLock.unlock();
        prn("High Priority Task closed");
    }
}

public class LowPriorityContinuousThread extends Thread {
    void getResourceSemaphore(){
        groupLock.lock();
        resourceSemaphore.acquireUninterruptibly();
        lowPriorRunFlag = true;
        groupLock.unlock();
        accessResource("low priority init");//here it is initialization and I want to happen only on request from priority thread
    }
    void releaseResourceSemaphore(){
        accessResource("low priority de-init");//here it is de-initialization and I want to happen only on request from priority thread
        lowPriorRunFlag = false;
        resourceSemaphore.release();
    }

    @Override
    public void run() {
        while(true){
            //prn("Low Priority Run req: "+lowPriorRunReq.get());
            if(lowPriorRunReq.get() == true && lowPriorRunFlag == false){
                prn("Low  Priority Task starting");
                getResourceSemaphore();
                prn("Low  Priority Task started");
            }
            if(lowPriorRunReq.get() == false && lowPriorRunFlag == true){
                prn("Low  Priority Task stopping");
                releaseResourceSemaphore();
                prn("Low  Priority Task stopped");
                lowPriorRunReq.smartWait(true);
            }
           //note keep checking lowPriorRunFlag. Imagine there is RunFlag detected by high priority thread
           //before de-asserted, then pauseReq would be requested from high priority thread
           //then resource is released when low priority task stops.
           //High priority lock and use resource, but since pauseReq is set
           //this thread would try to access device in order to de-init and pause (unless we check runFlag)
            if(lowPriorPauseReq.get() == true && lowPriorRunFlag == true){
                prn("Low  Priority Task pausing");
                releaseResourceSemaphore();
                prn("Low  Priority Task paused");
                lowPriorPauseReq.smartWait(false);
                getResourceSemaphore();
                prn("Low  Priority Task continue");
            }
            if(lowPriorRunFlag){
                accessResource("low priority task");
            }
        }
    }
}

Et voici plein de code Java compilable incluant un banc de test (donc j'ai une indication que c'est une solution sûre - mais on ne sait jamais avec ces threads)

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
   //this inner class is only for setting flag and waiting to it by notify - which does not hog CPU
    public class FlagNotify{
        private Boolean flag;//do not synchro on Boolean - it is immutable, thus new object is created every value change....
        private Object synchro = new Object();

        public FlagNotify(boolean val) {
            flag = val;
        }

        public void setNotify(boolean val) {
            synchronized (synchro) {
                flag = val;
                synchro.notify();
            }
        }

        public boolean get(){
            return flag;
        }
        public void smartWait(boolean expVal){
            synchronized (synchro){
                while(flag != expVal){
                    try {
                        synchro.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    FlagNotify lowPriorRunReq   = new FlagNotify(false);//low priority task run request
    FlagNotify lowPriorPauseReq = new FlagNotify(false);//low priority task pause request (it uses notify)
    volatile boolean lowPriorRunFlag = false; //low priority task run flag
    Lock      groupLock        = new ReentrantLock(); //group lock (used to acquire lowPriorRunFlag always correctly)
    Semaphore resourceSemaphore = new Semaphore(1);   //main semaphore protecting resource that has to be accessed sequentially

    public class PrioritySingleTaskThread extends Thread {
        @Override
        public void run() {
            prn("High Priority Task created");
            groupLock.lock();
            if(lowPriorRunFlag == true) lowPriorPauseReq.setNotify(true);
            resourceSemaphore.acquireUninterruptibly();
            groupLock.unlock();
            accessResource("high priority task");
            resourceSemaphore.release();
            groupLock.lock();
            if(lowPriorPauseReq.get() == true) lowPriorPauseReq.setNotify(false);
            groupLock.unlock();
            prn("High Priority Task closed");
        }
    }

    public class LowPriorityContinuousThread extends Thread {
        void getResourceSemaphore(){
            groupLock.lock();
            resourceSemaphore.acquireUninterruptibly();
            lowPriorRunFlag = true;
            groupLock.unlock();
            accessResource("low priority init");//here it is initialization and I want to happen only on request from priority thread
        }
        void releaseResourceSemaphore(){
            accessResource("low priority de-init");//here it is de-initialization and I want to happen only on request from priority thread
            lowPriorRunFlag = false;
            resourceSemaphore.release();
        }

        @Override
        public void run() {
            while(true){
                //prn("Low Priority Run req: "+lowPriorRunReq.get());
                if(lowPriorRunReq.get() == true && lowPriorRunFlag == false){
                    prn("Low  Priority Task starting");
                    getResourceSemaphore();
                    prn("Low  Priority Task started");
                }
                if(lowPriorRunReq.get() == false && lowPriorRunFlag == true){
                    prn("Low  Priority Task stopping");
                    releaseResourceSemaphore();
                    prn("Low  Priority Task stopped");
                    lowPriorRunReq.smartWait(true);
                }
               //note keep checking lowPriorRunFlag. Imagine there is RunFlag detected by high priority thread
               //before de-asserted, then pauseReq would be requested from high priority thread
               //then resource is released when low priority task stops.
               //High priority lock and use resource, but since pauseReq is set
               //this thread would try to access device in order to de-init and pause (unless we check runFlag)
                if(lowPriorPauseReq.get() == true && lowPriorRunFlag == true){
                    prn("Low  Priority Task pausing");
                    releaseResourceSemaphore();
                    prn("Low  Priority Task paused");
                    lowPriorPauseReq.smartWait(false);
                    getResourceSemaphore();
                    prn("Low  Priority Task continue");
                }
                if(lowPriorRunFlag){
                    accessResource("low priority task");
                }
            }
        }
    }
//-------------------------------------------------------------------------
//-- following functions are meant only for testing
    AtomicInteger clashDetector = new AtomicInteger(0);//only for testing purposes

    public void accessResource(String from){
        prn("Resource used from "+from);
        if(clashDetector.addAndGet(1)>1){
            System.out.println("Clash detected - you are a bad programmer :((((((");
            System.exit(-1);
        }
        sleepRandom(5);
        clashDetector.getAndAdd(-1);
    }

    public void sleepRandom(long maxMiliSec){
        mySleep((long)(Math.random()*maxMiliSec));
    }

    public void mySleep(long miliSec){
        try{
            Thread.sleep(miliSec);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    void prn(String s){
        System.out.println(s);
    }

    public void test(){
        new LowPriorityContinuousThread().start();
        for(long i=0; i< (long)1e3; i++){
            lowPriorRunReq.setNotify(true);
            for(int j=0; j
0
Comment passer en revue mon code ne répond pas - surtout si je demande si ce code est thread safe? Et je crois aussi que c'est un modèle assez répétable réutilisable par d'autres. C'est en somme une tâche continue de priorité basse qui peut être mise en pause à la demande par le partage de tâches prioritaires d'une ressource commune - j'ai été surpris de ne pas trouver un tel code prêt à l'emploi ...
ajouté l'auteur Vit Bernatik, source
Eh bien, il s'agit d'une question de définition de "spécifique". Croyez-moi, le code actuel a des lignes de plus de 13K et je ne veux pas que la communauté revoie ça;). Ce que j'ai créé était un exemple de modèle que je crois peut être réutilisable pour les autres. Récemment, j'ai commencé à lire un livre intitulé «head first design patterns» et à la fin de chaque chapitre, il s'agissait du même code de longueur. Et je pense que l'auteur dirait que c'est un modèle et non un code unique. Si vous dépouillez mon code du commentaire/des impressions, il est inférieur à 50 lignes. Le reste du code e
ajouté l'auteur Vit Bernatik, source
Je vote pour clore cette question hors sujet parce que c'est une demande de «revoir mon code», pas une question à laquelle je réponds.
ajouté l'auteur David Schwartz, source
"Passer en revue mon code" n'est pas une question, même si vous essayez d'en faire un en mettant un point d'interrogation après. Même si vous posez une question, mais qu'il s'agit d'un long code réel (pas un exemple complet démontrant un problème), ce n'est pas vraiment une question spécifique à propos d'une chose spécifique. Voici un test simple: Si vous ne pouvez pas démontrer le problème que vous posez avec un exemple de code minimal, vous n'avez probablement pas de question spécifique sur un problème spécifique.
ajouté l'auteur David Schwartz, source
Pour être juste, je pense que cette affaire est proche de la ligne. D'autres personnes pourraient certainement être en désaccord et penser que c'est de l'autre côté de la ligne.
ajouté l'auteur David Schwartz, source

2 Réponses

Comme James Large a suggéré d'utiliser Turnstile dans sa réponse . J'ai réécrit le code et il semble plus agréable au moins pour moi. J'ai donc fait DoubleTurnstile, il ressemble à ceci:

public class DoubleTurnstile{
    Semaphore  resourceSemaphore = new Semaphore(1);   //main semaphore protecting resource that has to be accessed sequentially
    volatile boolean  lowPriorRunReq    = false;//low priority task run request
    volatile boolean lowPriorPauseReq   = false;//low priority task pause request (it uses notify)
    private Object notifyWait = new Object();
    Lock       groupLock         = new ReentrantLock(); //group lock (used to acquire lowPriorRunFlag always correctly)
    volatile boolean lowPriorRunFlag = false; //low priority task run flag

    private void myWaitNotify(){
        synchronized (notifyWait){
            try {
                notifyWait.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private void myNotify(){
        synchronized (notifyWait){
            notifyWait.notify();
        }
    }

    public void highPriorityEnter() {
        groupLock.lock();
        //if (lowPriorRunFlag){//"if" is not necessary, but correct
            lowPriorPauseReq = true;
        //}
        resourceSemaphore.acquireUninterruptibly();
        groupLock.unlock();
    }

    public void highPriorityLeaves() {
        resourceSemaphore.release();
        groupLock.lock();
        if(lowPriorPauseReq == true){
            lowPriorPauseReq = false;
            myNotify();
        }
        groupLock.unlock();
    }

    public void lowPriorityLoopEnter() {
        while(true){
            if((lowPriorRunReq == true)
                && (lowPriorPauseReq == false)) break;
            myWaitNotify();
        }
        groupLock.lock();
        resourceSemaphore.acquireUninterruptibly();
        lowPriorRunFlag = true;
        groupLock.unlock();
    }

    public boolean lowPriorityLoop_ShallEnd() {
        return (lowPriorRunReq == false)
                || (lowPriorPauseReq == true);
    }
    public void lowPriorityLoop_Leaves() {
        lowPriorRunFlag = false;
        resourceSemaphore.release();
    }

    public void masterLowPriorityRunEn(boolean shallRun) {
        lowPriorRunReq = shallRun;
        myNotify();
    }
}

La tâche prioritaire utilise DoubleTurnstile comme suit:

public class PrioritySingleTaskThread extends Thread {
    @Override
    public void run() {
        prn("High Priority Task created");
        dblTurnstile.highPriorityEnter();
        accessResource("high priority task");
        dblTurnstile.highPriorityLeaves();
        prn("High Priority Task closed");
    }
}

et le thread continu de faible priorité l'utilise comme ceci:

public class LowPriorityContinuousThread extends Thread {
    @Override
    public void run() {
        while(true){
            dblTurnstile.lowPriorityLoopEnter();
            accessResource("low priority init");//here it is initialization and I want to happen only on request from priority thread
            while(true){
                accessResource("low priority task");
                if(dblTurnstile.lowPriorityLoop_ShallEnd() == true){
                    accessResource("low priority de-init");//here it is de-initialization and I want to happen only on request from priority thread
                    dblTurnstile.lowPriorityLoop_Leaves();
                    break;
                }

            }
        }
    }
}

Maintenant, il ressemble plus à un motif et plus facilement réutilisable.

Le code compilable complet avec la méthode de test est le suivant:

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main2 {

    DoubleTurnstile dblTurnstile = new DoubleTurnstile();

    public class DoubleTurnstile{
        Semaphore  resourceSemaphore = new Semaphore(1);   //main semaphore protecting resource that has to be accessed sequentially
        volatile boolean  lowPriorRunReq    = false;//low priority task run request
        volatile boolean lowPriorPauseReq   = false;//low priority task pause request (it uses notify)
        private Object notifyWait = new Object();
        Lock       groupLock         = new ReentrantLock(); //group lock (used to acquire lowPriorRunFlag always correctly)
        volatile boolean lowPriorRunFlag = false; //low priority task run flag

        private void myWaitNotify(){
            synchronized (notifyWait){
                try {
                    notifyWait.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        private void myNotify(){
            synchronized (notifyWait){
                notifyWait.notify();
            }
        }
       //call highPriorityLeaves() after used shared resources
        public void highPriorityEnter() {
            groupLock.lock();
            //if (lowPriorRunFlag){//"if" is not necessary but correct
                lowPriorPauseReq = true;
            //}
            resourceSemaphore.acquireUninterruptibly();
            groupLock.unlock();
        }
       //call exactly once for each previous highPriorityEnter()
        public void highPriorityLeaves() {
            resourceSemaphore.release();
            groupLock.lock();
            if(lowPriorPauseReq == true){
                lowPriorPauseReq = false;
                myNotify();
            }
            groupLock.unlock();
        }

        public void lowPriorityLoopEnter() {
            while(true){
                if((lowPriorRunReq == true)
                    && (lowPriorPauseReq == false)) break;
                myWaitNotify();
            }
            groupLock.lock();
            resourceSemaphore.acquireUninterruptibly();
            lowPriorRunFlag = true;
            groupLock.unlock();
        }

        public boolean lowPriorityLoop_ShallEnd() {
            return (lowPriorRunReq == false)
                    || (lowPriorPauseReq == true);
        }
        public void lowPriorityLoop_Leaves() {
            lowPriorRunFlag = false;
            resourceSemaphore.release();
        }

        public void masterLowPriorityRunEn(boolean shallRun) {
            lowPriorRunReq = shallRun;
            myNotify();
        }
    }

    public class PrioritySingleTaskThread extends Thread {
        int id;
        public PrioritySingleTaskThread(int id){this.id=id;}
        @Override
        public void run() {
            prn("High Priority Task created");
            dblTurnstile.highPriorityEnter();
            accessResource("high priority task",true, id);
            dblTurnstile.highPriorityLeaves();
            prn("High Priority Task closed");
        }
    }

    public class LowPriorityContinuousThread extends Thread {
        public int id = 0;
        @Override
        public void run() {
            while(true){
                dblTurnstile.lowPriorityLoopEnter();
                accessResource("low priority init",false,id++);//here it is initialization and I want to happen only on request from priority thread
                while(true){
                    accessResource("low priority task",false,id++);
                    if(dblTurnstile.lowPriorityLoop_ShallEnd() == true){
                        accessResource("low priority de-init",false,id++);//here it is de-initialization and I want to happen only on request from priority thread
                        dblTurnstile.lowPriorityLoop_Leaves();
                        break;
                    }

                }
            }
        }
    }

    //-------------------------------------------------------------------------
    //-- following functions are meant only for testing
    AtomicInteger clashDetector = new AtomicInteger(0);//only for testing purposes
    int hiPriorityCnt;//only for testing purposes
    int loPriorityCnt;//only for testing purposes
    int lastLowPriorityId=-1;
    int lastHiPriorityId=-1;
    int hiPriorityOutOfOrder=0;

    public void accessResource(String from,boolean hiPriority, int id) {
        prn("Resource used from " + from+" id: "+id);
        if(hiPriority){
            if( (id - lastHiPriorityId) < 1) {
               //note if id - lastHiPriorityId=+2 (one sample over-jumped) it will be detected
               //when returned to the over-jumped sample,
               //or at the end of the test one sample missing will be detected
               //so no injustice will escape it's punishment ;)
               //On the other hand if we want strictly ==1 then one error will be reported 3 times -
               //1st when ID: 1->3
               //2nd when ID: 3->2//correct error
               //3rd when ID: 2->4
                System.out.println("High priority jumped over each other - it's not nice but it can happen");
                hiPriorityOutOfOrder++;
            }
            lastHiPriorityId = id;
            hiPriorityCnt++;
        }
        else{
            if( (id - lastLowPriorityId) < 1) {
                System.out.println("LowPriorityLoop request swapped - you are a bad programmer :((((((");
                System.exit(-1);
            }
            lastLowPriorityId = id;
            loPriorityCnt++;
        }
        if (clashDetector.addAndGet(1) > 1) {
            System.out.println("Clash detected - you are a bad programmer :((((((");
            System.exit(-1);
        }
        sleepRandom(5);
        clashDetector.getAndAdd(-1);
    }

    public void sleepRandom(long maxMiliSec) {
        mySleep((long) (Math.random() * maxMiliSec));
    }

    public void mySleep(long miliSec) {
        try {
            Thread.sleep(miliSec);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    void prn(String s) {
        System.out.println(s);
    }

    public void test() {
        int idHiPriority = 0;
        LowPriorityContinuousThread lowPriorThrd = new LowPriorityContinuousThread();
        lowPriorThrd.start();
        for (long i = 0; i < (long) 1e3; i++) {
            dblTurnstile.masterLowPriorityRunEn(true);
            for (int j = 0; j < Math.random() * 100; j++) {
                sleepRandom(10);
                new PrioritySingleTaskThread(idHiPriority++).start();
            }
            //sleepRandom(20);
            dblTurnstile.masterLowPriorityRunEn(false);

            for (int j = 0; j < Math.random() * 20; j++) {
                sleepRandom(10);
                new PrioritySingleTaskThread(idHiPriority++).start();
            }
            //sleepRandom(20);
        }
        mySleep(500);
        boolean testOk = true;
        if(hiPriorityCnt != idHiPriority){
            System.out.println(String.format("Error hiPriorityCnt(%d) != idHiPriority(%d)",
                    hiPriorityCnt, idHiPriority));
            testOk = false;
        }
        if(loPriorityCnt != lowPriorThrd.id){
            System.out.println(String.format("Error loPriorityCnt(%d) != lowPriorThrd.id(%d)",
                    loPriorityCnt, lowPriorThrd.id));
            testOk = false;
        }
        System.out.println("High priority tasks performed: "+hiPriorityCnt);
        System.out.println("High priority out of order: "+hiPriorityOutOfOrder);
        System.out.println("Low priority tasks performed: "+loPriorityCnt);
        if(testOk){
            System.out.println("Test2 OK :)))))))))))))))))))))");
        }else{
            System.out.println("Test Failed :(((((((((((((((((((((");
        }
        mySleep(200);
        System.exit(0);
    }

    public static void main(String[] args) th
0
ajouté

Je ne sais pas ce que cela signifie de "démarrer/arrêter" un "fil pour toujours", mais un modèle de conception que vous pouvez utiliser pour suspendre un fil est appelé un "tourniquet". En voici un qui fonctionne quand il n'y a qu'un seul thread controller autorisé à "verrouiller" ou "déverrouiller" le tourniquet:

import java.util.concurrent.Semaphore;

class Turnstile {
    private final Semaphore semaphore = Semaphore.new(1);

   //Called only from the "controller" thread.
    public void lock() {
        semaphore.acquire();
    }

   //Called only from the "controller" thread.
    public void unlock() {
        semaphore.release();
    }

   //Called from worker threads.
    public void passThrough() {
        semaphore.lock();
        semaphore.release();
    }
}

Initialement, le tourniquet est dans l'état "déverrouillé", et la méthode passThrough() retourne rapidement quand un travailleur l'appelle. Si le thread principal "verrouille" le tourniquet, alors tout ouvrier qui appelle passThrough() sera bloqué jusqu'à ce que le maître le "déverrouille" à nouveau. Ensuite, tous les travailleurs vont «passer», un par un.


Vous pouvez modifier cet exemple si vous voulez avoir plus d'un "maître", mais c'est à vous de décider comment résoudre le conflit lorsqu'un maître veut que le tourniquet soit verrouillé, et un autre veut qu'il soit déverrouillé.


Là encore, au lieu de modifier Turnstile , vous pouvez écrire un nouveau MultiMasterTurnstile qui gère la résolution de conflit et utilise Turnstile pour bloquer réellement les travailleurs.

0
ajouté
Hey c'est loin de mes contraintes. Mais en fait, cela semble intéressant. La chose principale est que j'ai un thread qui est la plupart du temps en cours d'exécution et j'ai besoin de lui dire de mettre en pause (et avant la ressource partagée de-init). Mais c'est une bonne idée de centraliser toute la décision à une classe et ensuite cette tâche continue n'appellerait qu'une méthode shall_I_Stop() et ensuite shalI_I_Start (). Ensuite, la tâche à priorité élevée appelle la méthode passThrough (). Ouais c'est vraiment une idée intéressante ... Thx ... Je me demande si je dois prendre un tracas
ajouté l'auteur Vit Bernatik, source
C'est un bon point. Si vous regardez mon code, vous voyez que j'utilise wait() - notify() pour éviter l'interrogation. Mais vous avez raison le bon serait passThroughHighPriority (), passThroughLowPriority() (au lieu de shall_I_start). shall_I_stop() resterait. Sur "oui", il désinitialiserait la ressource et appellerait passThroughLowPriority() sur "non" il continuerait à utiliser la ressource. Probablement le code serait très similaire à ce que j'ai fait mais la décision serait dans une classe, difficile à deviner avant que j'essaie de mettre en œuvre ...
ajouté l'auteur Vit Bernatik, source
Oh, je viens de remarquer qu'il y a un gros défaut dans le tourniquet. Il laisse les threads un par un - mais après le tourniquet, ils peuvent fonctionner simultanément. J'aurais besoin d'un double tourniquet - ne laissant entrer qu'un seul fil à la fois. C'est laisser un autre fil quand le premier fil part. J'essaierai de le faire...
ajouté l'auteur Vit Bernatik, source
Hé donc j'ai réécrire ça à DoubleTurnstile et ça a l'air plus propre et aussi plus réutilisable maintenant. Merci. Toujours pouvez-vous s'il vous plaît regarder ma réponse et dire si elle vous semble sûre thread? Le banc d'essai simple passe donc il peut être thread safe ...
ajouté l'auteur Vit Bernatik, source
@VitBernatik, que fait-il s'il appelle shall_I_Start (), et la réponse est non? Est-ce que ça réessaye? À quelle fréquence? Cela ressemble à interroger, et l'interrogation est souvent une mauvaise idée (sauf si vous écrivez des choses du noyau du système d'exploitation de bas niveau). En utilisant le modèle Turnstile , votre thread de travail appelle la méthode passThrough() à des moments appropriés, et il passera immédiatement si le tourniquet est déverrouillé ou attendra jusqu'à ce que le tourniquet soit déverrouillé.
ajouté l'auteur james large, source