Difficult race situation

I have this race condition with an audio playback class, where every time I start playback, I set keepPlayingit to true and false when I stop.

The problem occurs when I stop () immediately after starting, and the flag is keepPlayingset to false, and then reset to true.

I could put the delay in stop (), but I don't think this is a very good solution. Should I use a conditional variable to do stop () until keepPlayingit is true?

How do you usually solve this problem?

#include <iostream>
#include <thread>
using namespace std;


class AudioPlayer

{
    bool keepRunning;
    thread thread_play;

    public: 
    AudioPlayer(){ keepRunning = false; }
    ~AudioPlayer(){ stop(); }

    void play()
    {
        stop();
        // keepRunning = true; // A: this works OK
        thread_play = thread(&AudioPlayer::_play, this);
    }
    void stop()
    {
        keepRunning = false;
        if (thread_play.joinable()) thread_play.join();
    }
    void _play()
    {
        cout << "Playing: started\n";
        keepRunning = true; // B: this causes problem
        while(keepRunning)
        {
            this_thread::sleep_for(chrono::milliseconds(100)); 
        }
        cout << "Playing: stopped\n";
    }
};


int main()
{
    AudioPlayer ap;

    ap.play();
    ap.play();
    ap.play();

    return 0;
}

Conclusion:

$. / test
Game: started
(pause endlessly ...)

+4
6

, :

1) keepRunning , , .

2) atomic_bool, , .

class AudioPlayer
{
    thread thread_play;

public:
    AudioPlayer(){ }
    ~AudioPlayer()
    {
        keepRunning = false;
        thread_play.join();

    }

    void play()
    {
        unique_lock<mutex> l(_mutex);
        keepRunning = false;
        if ( thread_play.joinable() )
            thread_play.join();
        keepRunning = true;
        thread_play = thread(&AudioPlayer::_play, this);
    }
    void stop()
    {
       unique_lock<mutex> l(_mutex);
       keepRunning = false;
    }
private:
    void _play()
    {
        cout << "Playing: started\n";
        while ( keepRunning == true )
        {
            this_thread::sleep_for(chrono::milliseconds(10));
        }
        cout << "Playing: stopped\n";
    }

    atomic_bool keepRunning { false };
    std::mutex _mutex;
};




int main()
{
    AudioPlayer ap;
    ap.play();
    ap.play();
    ap.play();
    this_thread::sleep_for(chrono::milliseconds(100));
    ap.stop();
    return 0;
}
+2

.

keepPlaying=true A , B .

ap.stop() ( ) B ( ), .

keepRunning , , . , "" , , - . std::mutex.

.join() stop(). , . , , main() (*), ( ).

"play", / . , "" /.

#include <iostream>
#include <thread>
#include <atomic>

class AudioPlayer

{
    std::atomic<bool> keepRunning;
    std::thread thread_play;

    public: 
    AudioPlayer():keepRunning(false){ 
    }
    ~AudioPlayer(){ stop(); }

    void play()
    {
        stop();
        keepRunning = true; // A: this works OK
        thread_play = std::thread(&AudioPlayer::_play, this);
    }

    void stop()
    {
        keepRunning=false;
        if (thread_play.joinable()){
            thread_play.join(); 
        } 
    }
    void _play()
    {
        std::cout<<"Playing: started\n";
        while(keepRunning)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
        }
        std::cout<<"Playing: stopped\n";
    }
};


int main()
{
    AudioPlayer ap;

    ap.play();
    ap.play();
    ap.play();
    ap.stop();
    return 0;
}

(*) detach(), .

+2

keepPlaying , ( play()). .

, condition_variable notify_one notify_all, wait_until 0. cv_status::timeout, .

stop , . , .

, undefined , . atomic<bool> , , .

+1

-, , , - keepRunning, . , play, . , keepRunning a std::atomic<bool>.

play stop - play , - . , :

  • keepRunning
  • play , ( if stop).

, , reset, , - . , , ( CAS).

+1

, . :

1: bool 2

, , , undefined. undefined , . Snps SO:

, , bool x86 :

2:

, . , " " keepRunning , , . ++ (98, 2003) volatile , / . , " " while, keepRunning , stop() .

, ++ 11 atomic atomic_bool, , non-cachable / , 1 2.

: volatile , Dr. Dobbs, :

- , , . C ++ . volatile , - .

3: , _play()

, OS . , , . "main thread" play() " ". , _play(). keepRunning true.

, play() _play(). A condition_variable . play() , _play() , .

:

#include <iostream>
#include <thread>
#include <atomic>

using namespace std;

class AudioPlayer  
{
    atomic_bool keepRunning;
    thread thread_play;
    std::mutex mutex;
    std::condition_variable play_started;

public: 
    AudioPlayer()
      : keepRunning{false}
    {}

    ~AudioPlayer(){ stop(); }

    void play()
    {
        stop();
        std::unique_lock<std::mutex> lock(mutex);
        thread_play = thread(&AudioPlayer::_play, this);
        play_started.wait(lock);
    }
    void stop()
    {
        keepRunning = false;
        cout << "stop called" << endl;
        if (thread_play.joinable()) thread_play.join();
    }
    void _play()
    {
        cout << "Playing: started\n";
        keepRunning = true; // B: this causes problem
        play_started.notify_one();
        while(keepRunning)
        {
            this_thread::sleep_for(chrono::milliseconds(100)); 
        }
        cout << "Playing: stopped\n";
    }
};


int main()
{
    AudioPlayer ap;

    ap.play();
    ap.play();
    ap.play();

    return 0;
}
+1

A . - undefined , , . keepRunning atomic<bool>. A, . , stop , ( , _play) .

, . play stop . AudioPlayer AudioPlayer , .

0

Source: https://habr.com/ru/post/1676977/


All Articles