2016-08-12 4 views
0

Ich mache ein Timer-Programm, das die seit dem Start des Programms vergangene Zeit zählt. Im Hintergrund überprüfe ich auch auf Tastatureingabe (Enter/zurück zum Ausgang, klicke auf das Fenster); Dies geschieht in einem separaten Thread, den ich als gelöst ausgeführt habe.Benutzereingaben von einem anderen Thread übernehmen

Es scheint, dass der zweite Thread die Eingabe vom Hauptthread nicht empfangen kann. Wenn ich die Tastatur oder die Maus benutze, passiert nichts. Außerdem erscheint nichts auf dem Bildschirm, nur weiß.

std::mutex g_mutex; 
std::condition_variable cv; 

// check for input from the user using the window object 
// sets stopProgram to true if the user wishes to exit 
void poll(sf::RenderWindow& window, bool& stopProgram) { 
    std::unique_lock<std::mutex> lk(g_mutex); 
    // wait for main thread to open window 
    cv.wait(lk, [&] { return !stopProgram && window.isOpen(); }); 
    sf::Event event; 
    while (true) { 
     if (window.pollEvent(event)) { 
      // if user wants to exit program 
      if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && 
       (event.key.code == sf::Keyboard::Return || event.key.code == sf::Keyboard::Escape))) { 
       window.close(); 
       // main thread will explicitly exit the main loop 
       stopProgram = true; 
       break; 
      } 
     } 
    } 
} 

int main() 
{ 
    int hour = 0, minute = 0, second = 0; 
    auto text = textObject(); 
    bool stopProgram = false; 
    // run a background thread that checks for input while the main program runs 
    std::thread(poll, std::ref(window), std::ref(stopProgram)).detach(); 
    std::once_flag flag; 

    std::lock_guard<std::mutex> lk(g_mutex); 
    while (window.isOpen()) { 
     // notify once window opens 
     std::call_once(flag, [&] { cv.notify_one(); }); 
     // set timestamp 
     text->setString(makeTimeStamp(hour, minute, second)); 
     // if the background thread set stopProgram, end the program 
     if (stopProgram) break; 
     window.clear(sf::Color::White); 
     window.draw(*text); 
     window.display(); 
     // update time 
     second = (second + 1) % MAX_SEC; 
     if (second == 0) minute = (minute + 1) % MAX_MIN; 
     if (second == 0 && minute == 0) hour = (hour + 1) % MAX_HOUR; 
     // sleep one second 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
    } 
} 

Ist meine Verwendung von Multithreading richtig? Wenn ja, kann nur der Haupt-Thread eine Eingabe erhalten und deshalb funktioniert es nicht?

Update: Wenn ich die while (true) und while (window.pollEvent(event)) loszuwerden verwenden und die lock_guard bis kurz vor if (stopProgram) dann wird der Text (Zeitstempel) erscheint auf dem Bildschirm bewegen, aber ich kann immer noch nicht Eingang verarbeiten.

+0

Aufruf von 'std :: this_thread :: sleep_for (std :: chrono :: Sekunden (1)); 'bedeutet nicht unbedingt, dass Ihr Programm in 1 Sekunde fortgesetzt wird. Also, wenn Sie eine genaue Zählung möchten, überprüfen Sie die Differenz zwischen der aktuellen Zeit und der vorherigen. –

+0

"unique_lock" wird auch nur mit geteilten Mutexes verwendet. Die Verwendung von unique_lock bei nicht-gemeinsam genutzten Mutex ruft grundsätzlich 'lock_guard' auf. Sie sollten über freigegebene Mutexe auf http://en.cppreference.com/w/cpp/thread/shared_mutex lesen –

+0

@TalShalti Wie überprüfe ich, ob der Unterschied in der Zeit genau eine Sekunde ist? –

Antwort

1

Haupt-Thread startet Poll-Thread.

std::thread(poll, std::ref(window), std::ref(stopProgram)).detach(); 

Hauptthread erwirbt g_mutex und nie gibt sie immer.

std::lock_guard<std::mutex> lk(g_mutex); 

Umfrage Thread wartet auf g_mutex freigegeben werden:

std::unique_lock<std::mutex> lk(g_mutex); 

aber der Haupt-Thread nie gibt sie, so die Umfrage Thread tut nie etwas.

Um es zu beheben. Ändern Sie den Beginn der main() Funktion:

int main() 
{ 
    int hour = 0, minute = 0, second = 0; 
    auto text = textObject(); 
    volatile bool stopProgram = false; 
    // run a background thread that checks for input while the main program runs 
    std::thread(poll, std::ref(window), std::ref(stopProgram)).detach(); 

    while (!window.isOpen()) { /* busy loop */ } 
    { 
     std::lock_guard<std::mutex> lk(g_mutex); 
     cv.notify_all(); 
    } 
    while (window.isOpen()) { 
     ... 

Diese SFML API Dinge schwieriger als andere Windowing-Frameworks macht, die ich benutzt habe. Es wäre so nützlich, wenn es eine thread-sichere Funktion window.pushCustomEvent() gäbe.

+0

Was geht in '/ * busy loop * /' und warum ist 'stopProgram' jetzt' volatile'? –

+0

Nichts geht in/* busy loop * /. Der Kommentar sagt dem Leser, dass ich nicht versehentlich eine While-Schleife geschrieben habe, in der nichts enthalten ist. Ich wollte eine While-Schleife schreiben, in der nichts ist. –

Verwandte Themen