2015-07-27 2 views
5

У меня есть приложение, которое считывает данные со стандартного ввода, используя getline() в потоке. Я хочу закрыть приложение из основного потока, в то время как getline все еще блокирует другой поток. Как это можно достичь?Выход из приложения при блокировке stdin на окнах

Я не хочу, чтобы пользователи вынуждены были нажать ctrl-Z, чтобы закрыть stdin и приложение.

я пытался до сих пор с моими настройками компилятором (библиотека времени исполнения =/MT) на ОС Windows 8.1 64bit, v120 платформа набор инструментов:

  • freopen STDIN, но он заблокирован внутренним замком
  • разрушающих нить , который вызывает преждевременное прекращение()
  • Putback Eof, линия конец STD :: CIN, который также заблокирован

* Update *

  • открепление() не работает, выход() блокируется замком
  • WinAPI TerminatThread() вызывает прервать()
  • WinAPI CloseHandle (GetStdHandle (STD_INPUT_HANDLE)) висит
  • призывающую TerminateProcess() - работает, но я хотел бы, чтобы выйти изящно

* Update 2: Решение *

  • WriteConsoleInput() может преобразовать std :: getline() из блокировки чтения. Это работает с любым msvc runtime libray. Для кода рабочего решения см. Принятый ответ.

Пример кода, показывающий эту проблему:

#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

[деструктор потока] (http://en.cppreference.com/w/cpp/thread/thread/~thread) вызывает станд :: прекратить, если вы не 'отсоединять()' или 'присоединиться() 'это раньше. – Drop

+0

Есть ли причина, почему вы выполняете динамическое распределение? – CoffeeandCode

ответ

2

writeConsoleInput() может сделать зЬй :: GetLine возвращения из блокировки чтения, поэтому он может решить эту проблему, даже если опция компилятора/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

Просто отрывать нить:

#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; 
} 

уборщик способы являются

  • Если STDIN получает пользовательский ввод, иметь правильный выход в потоке (не завершать интерактивный ввод, из синего)
  • Неблокируемая считывает данные из стандартного ввода (который зависит от системы)
  • Настройка трубопровода
  • Используя гнездо
+0

Уже пробовал. Приложение не выходит из этого способа. Ожидает блокировку в _locterm(). – simon

+0

Мне удалось заставить этот подход работать в моем коде, имея блок чтения строк stdin в ReadFile(), а не getline(). Кажется, все работает хорошо для меня (хотя я думаю, что это ужасный взлом, чтобы оставить поток, который работает так). Соответствующий код можно увидеть, начиная со строки 28 этого файла: https://public.msli.com/lcs/muscle/muscle/dataio/StdinDataIO.cpp –

0

Там не является стандартным и даже кросс-платформенным решением для прерывания std:cin или std::thread. В обоих случаях вам потребуется использовать API-интерфейсы, специфичные для ОС. Вы могли бы получить дескриптор, специфичный для ОС, для потока с std::thread::native_handle()

Как быстрый и грязный хак, вы могли бы просто detach нить. Но имейте в виду this и 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)); 
} 

также:

  • Нет необходимости выделять нить на куче:

    std::thread t([]{ 
    
    }); 
    
  • return 0; ненужно в C++
  • stop = true; вызовет ошибку компиляции, поскольку stop не объявлен в этой области
  • Если вы планируете делиться булевым флагом таким образом, у вас будет типичный race condition и, следовательно, UB
  • Вероятно, ближайший к «стандартному» или «кросс-платформенному» решению для неблокирующего ввода может быть ncurses (как есть * Никс и pdcurses на Windows)
+0

Правильно, слишком много прав ... Я исправил пример компилируется. – simon

+0

Отсоединение не работает, выход блокируется блокировкой. – simon

+0

@simon Что именно не работает? Что означает «выход заблокирован блокировкой»? Для меня программа запускается и выходит за 1 секунду на Linux (GCC 4.9.2) и Windows (VS2013u5). Какую платформу, компилятор и стандартную библиотеку вы используете? Пробовал ли вы мой фрагмент кода дословно? – Drop

0

Если ничего не работает, всегда есть ядерный вариант:

TerminateProcess(GetCurrentProcess(), 0); 

Просто убедитесь, что вы покраснел любой из среды выполнения буферов, которые вам небезразличны.

+0

Опция компилятора/MT также блокируется. Программа не выйдет. – simon

+0

Работает для меня, даже с/MT. В какой операционной системе вы работаете? –

+0

Windows 8.1 64bit – simon

0

Этот код является многопоточным флагом. Прежде всего, зачем создавать новый поток в куче? просто объявите его в стеке и вызовите std::thread::detach.
второй, кто пообещал вам, что stop в этом контексте будет работать? более чем возможно, что процессор кэширует это логическое значение и никогда не смотрит на реальный (если не частично оптимизировать его или другие компиляционные трюки ..). вам нужно сделать атомное:

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; 
} 

скомпилирован с визуальной студии 2013 на окнах 7 и работает, как ожидалось.

+0

Это был всего лишь пример кода, чтобы показать задачу, которую я хочу выполнить. Ваш код не будет работать с/компилятором MT, который я должен использовать. – simon

+0

, поэтому вашей проблемы нет. ваш реальный поток статичен для какого-то класса? –

0

Это работает для меня, хотя это немного нечестный:

#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

Не работает для меня. Вызов CloseHandle не возвращается. – simon

+0

Можете ли вы попробовать его на недавно установленной виртуальной машине? Я думаю, что может быть стороннее программное обеспечение (возможно, антивирусное). AFAIK, CloseHandle не должен блокироваться ни при каких обстоятельствах. –

+0

Еще один подход, я полагаю, состоял бы в том, чтобы вставить входные символы через WriteConsoleInput, чтобы getline выходил естественным образом. Довольно грязно. –