2015-07-27 14 views
5

Mam aplikację, która odczytuje dane ze standardowego wejścia za pomocą getline() w wątku. Chcę zamknąć aplikację z głównego wątku, podczas gdy getline nadal blokuje drugi wątek. Jak można to osiągnąć?Zamknij aplikację, gdy blokada wejścia w systemie Windows

Nie chcę zmuszać użytkowników do naciśnięcia klawisza ctrl-Z, aby zamknąć stdin i aplikację.

Próbowałem tak daleko z moich ustawień kompilator (RuntimeLibrary =/MT) w systemie Windows 8.1 64bit, v120 platforma ToolSet:

  • freopen stdin, ale jest zablokowany przez blokady wewnętrznej
  • złomowaniu wątek , który wywołuje abort()
  • putback EOF, end linia do std :: cin, która również zablokowany

* Aktualizacja *

  • odłączy() nie działa, exit() blokowane przez blokadę
  • winapi TerminatThread() wywołuje abort()
  • winapi CloseHandle (GetStdHandle (STD_INPUT_HANDLE)) zawiesza
  • wzywającą TerminateProcess() - działa, ale chciałbym zakończyć bezpiecznie

* UPDATE 2: Rozwiązanie *

  • Funkcja WriteConsoleInput() może spowodować, że std :: getline() zwróci blokowanie odczytu. Działa to z dowolnym libscem wykonawczym msvc. Aby zapoznać się z kodem roboczego rozwiązania, zobacz zaakceptowaną odpowiedź.

Przykładowy kod pokazujący problem:

#include <iostream> 
#include <thread> 
#include <string> 
#include <chrono> 

int main(int argc, char *argv[]) 
{ 
    bool stop = false; 
    std::thread *t = new std::thread([&]{ 
     std::string line; 
     while (!stop && std::getline(std::cin, line, '\n')) { 
      std::cout << line; 
     } 
    }); 

    std::this_thread::sleep_for(std::chrono::seconds(1)); 

    stop = true; 
    // how to stop thread or make getline to return here? 

    return 0; 
} 
+0

[destructor wątku] (http://en.cppreference.com/w/cpp/thread/thread/~thread) wywołuje std :: zakończyć, jeśli nie 'odłączyć()' lub 'przyłączyć() 'to wcześniej. – Drop

+0

Czy istnieje powód, dla którego dokonujesz alokacji dynamicznej? – CoffeeandCode

Odpowiedz

2

writeConsoleInput() może uczynić std :: getline zwrot blokowaniu czytać, więc może to rozwiązać problem, nawet gdy wykorzystywane opcji kompilatora/MT.

#include <Windows.h> 

#include <iostream> 
#include <thread> 
#include <string> 
#include <chrono> 
#include <atomic> 

int main(int argc, char *argv[]) 
{ 
    std::atomic_bool stop; 

    stop = false; 

    std::thread t([&]{ 
     std::string line; 
     while (!stop.load() && std::getline(std::cin, line, '\n')) { 
      std::cout << line; 
     } 
    }); 


    std::this_thread::sleep_for(std::chrono::seconds(1)); 

    stop = true; 

    DWORD dwTmp; 
    INPUT_RECORD ir[2]; 
    ir[0].EventType = KEY_EVENT; 
    ir[0].Event.KeyEvent.bKeyDown = TRUE; 
    ir[0].Event.KeyEvent.dwControlKeyState = 0; 
    ir[0].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN; 
    ir[0].Event.KeyEvent.wRepeatCount = 1; 
    ir[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; 
    ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC); 
    ir[1] = ir[0]; 
    ir[1].Event.KeyEvent.bKeyDown = FALSE; 
    WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dwTmp); 

    t.join(); 

    return 0; 
} 
0

Wystarczy odłączyć wątek:

#include <iostream> 
#include <thread> 
#include <chrono> 

bool stop = false; 
int main(int argc, char *argv[]) 
{ 
    std::thread t([]{ 
     bool stop = false; 
     std::string line; 
     while (!stop && std::getline(std::cin, line, '\n')) { 
      std::cout << line; 
     } 
    }); 

    std::this_thread::sleep_for(std::chrono::seconds(1)); 

    stop = true; 
    // Without detach: g++: terminate called without an active exception 
    t.detach(); 

    return 0; 
} 

Cleaner sposoby są

  • Jeśli stdin jest uzyskiwanie danych wprowadzonych przez użytkownika, mieć właściwe wyjście w wątku (nie zakończyć wprowadzanie interaktywnych, z niebieskim)
  • non-blocking czyta ze standardowego wejścia (która jest zależna od systemu)
  • Konfiguracja rurociągu
  • Korzystanie gniazdo
+0

Już próbowałem. Aplikacja nie wychodzi w ten sposób. Czeka na blokadę _locterm(). – simon

+0

Udało mi się uczynić to podejście działaniem w moim kodzie przez blok wątku czytania stdin w ReadFile() zamiast getline(). Wydaje mi się, że działa mi dobrze (chociaż myślę, że to okropny hack zostawić wątek działający w ten sposób). Odpowiedni kod można zobaczyć zaczynając od wiersza 28 tego pliku: https://public.msli.com/lcs/muscle/muscle/dataio/StdinDataIO.cpp –

0

Nie nie jest standardowym, a nawet wieloplatformowym rozwiązaniem do przerywania std:cin lub std::thread. Będziesz musiał użyć interfejsów API specyficznych dla systemu operacyjnego w obu przypadkach. Możesz pobrać specyficzny dla systemu uchwyt dla wątku z std::thread::native_handle()

Jako szybki i brudny hack możesz po prostu nić detach. Ale pamiętaj o this i that.

int main(int argc, char *argv[]) { 
    std::thread t([&] { 
     std::string line; 
     while (std::getline(std::cin, line, '\n')) { 
      std::cout << line; 
     } 
    }); 
    t.detach(); 

    std::this_thread::sleep_for(std::chrono::seconds(1)); 
} 

również:

  • Nie trzeba przeznaczyć wątek na stercie:

    std::thread t([]{ 
    
    }); 
    
  • return 0; jest zbędna w C++
  • stop = true; wywoła błąd kompilacji, jak stop nie jest zadeklarowana w tym zakresie
  • Jeżeli planujesz udostępnić logiczną flagi w ten sposób, trzeba będzie typowy race condition a zatem UB
  • Prawdopodobnie najbliżej „standard” lub „cross-platform” rozwiązanie dla non-blokowanie wejścia może być ncurses (jak na * nix i pdcurses Windows)
+0

Prawo, za dużo zmian ... Naprawiłem przykład, aby być kompilowany. – simon

+0

Odłączenie nie działa, wyjście jest blokowane przez zamek. – simon

+0

@imon Co dokładnie nie działa? Co to znaczy "wyjście jest zablokowane przez zamek"? Program uruchamia się i znika w ciągu 1 sekundy na obu systemach Linux (GCC 4.9.2) i Windows (VS2013u5). Jakiej platformy, kompilatora i standardowej lib używasz? Czy wypróbowałeś mój fragment kodu dosłownie? – Drop

0

Jeżeli nic innego nie działa, zawsze istnieje opcja jądrowa:

TerminateProcess(GetCurrentProcess(), 0); 

Tylko upewnij się, że spłukuje którykolwiek z buforów uruchomieniowych Ci zależy.

+0

Opcja kompilatora/MT również jest zablokowana. Program nie zostanie zakończony. – simon

+0

Działa dla mnie, nawet z/MT. Jakiego systemu operacyjnego używasz? –

+0

Windows 8.1 64bit – simon

0

ten kod jest wadliwy wielowątkowo. po pierwsze, po co tworzyć nowy wątek na stercie? po prostu zadeklaruj go na stosie i zadzwoń pod numer std::thread::detach.
sekunda, który obiecał, że w tym kontekście zadziała stop? jest bardziej niż możliwe, że procesor buforuje tę wartość logiczną i nigdy nie patrzy na rzeczywistą (jeśli nie częściowo ją optymalizuje lub inne sztuczki kompilujące ...). trzeba zrobić to atomowy:

int main(int argc, char *argv[]) 
{ 
    std::atomic_bool stop; 
    stop = false; 
    std::thread t([&]{ 
     std::string line; 
     while (!stop.load() && std::getline(std::cin, line, '\n')) { 
      std::cout << line; 
     } 
    }); 
    t.detach(); 
    stop = true; 
} 

skompilowany z Visual Studio 2013 na Windows 7 i działa zgodnie z oczekiwaniami.

+0

To był tylko przykładowy kod pokazujący zadanie, które chcę wykonać. Twój kod nie wyjdzie z przełącznikiem kompilatora/MT, którego muszę użyć. – simon

+0

, więc twój problem nie istnieje. czy twoja prawdziwa nić jest statyczna dla jakiejś klasy? –

0

Działa to dla mnie, chociaż to trochę podejrzanie:

#include <Windows.h> 

#include <iostream> 
#include <thread> 
#include <string> 
#include <chrono> 
#include <atomic> 

int main(int argc, char *argv[]) 
{ 
    std::atomic_bool stop; 

    stop = false; 

    std::thread t([&]{ 
     std::string line; 
     while (!stop.load() && std::getline(std::cin, line, '\n')) { 
      std::cout << line; 
     } 
    }); 

    std::this_thread::sleep_for(std::chrono::seconds(1)); 

    stop = true; 

    CloseHandle(GetStdHandle(STD_INPUT_HANDLE)); 

    t.join(); 

    return 0; 
} 
+0

Nie działa dla mnie. CloseHandle call nie wraca. – simon

+0

Czy możesz go wypróbować na świeżo zainstalowanej maszynie wirtualnej? Myślę, że może być wtrącanie oprogramowania innej firmy (być może antywirusa). AFAIK, CloseHandle nie powinien blokować w żadnych okolicznościach. –

+0

Jeszcze inne podejście, jak przypuszczam, polegałoby na wstawianiu znaków wejściowych poprzez WriteConsoleInput, tak aby getline wychodził naturalnie. Całkiem nieźle. –

Powiązane problemy