2015-07-27 15 views
5

Tôi có một ứng dụng đọc dữ liệu từ đầu vào tiêu chuẩn bằng cách sử dụng getline() trong một chuỗi. Tôi muốn đóng ứng dụng từ chủ đề chính, trong khi đường dây vẫn chặn luồng khác. Làm thế nào điều này có thể đạt được?Thoát ứng dụng trong khi chặn stdin trên cửa sổ

Tôi không muốn ép buộc người dùng phải nhấn ctrl-Z để đóng stdin và ứng dụng.

Tôi đã cố gắng cho đến nay với các thiết lập trình biên dịch của tôi (RuntimeLibrary =/MT) trên Windows 8.1 64bit, v120 nền tảng công cụ:

  • freopen stdin, nhưng nó bị chặn bởi khóa nội
  • huỷ sợi , trong đó kêu gọi hủy bỏ()
  • rút lại một EOF, dòng dấu chấm hết cho std :: cin, mà cũng bị chặn

* cập nhật *

  • Mở cửa sổ mới() không làm việc, exit() bị chặn bởi một khóa
  • WINAPI TerminatThread() cuộc gọi hủy bỏ()
  • WINAPI CloseHandle (GetStdHandle (STD_INPUT_HANDLE)) treo
  • gọi TerminateProcess() - làm việc, nhưng tôi muốn kết thúc tốt đẹp

* cập nhật 2: giải pháp *

  • WriteConsoleInput() có thể làm cho std :: getline() trở về từ chặn đọc. Điều này làm việc với bất kỳ thời gian chạy msvc libray nào. Đối với mã giải pháp làm việc, xem câu trả lời được chấp nhận.

Ví dụ mã hiển thị các vấn đề:

#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

[Thread's destructor] (http://en.cppreference.com/w/cpp/thread/thread/~thread) gọi std :: chấm dứt nếu bạn không 'tách()' hoặc 'tham gia() 'trước đó. – Drop

+0

Có lý do nào khiến bạn phân bổ động không? – CoffeeandCode

Trả lời

2

writeConsoleInput() có thể làm cho std :: getline trở về từ chặn đọc, vì vậy nó có thể giải quyết vấn đề ngay cả khi/MT trình biên dịch tùy chọn sử dụng.

#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

Chỉ cần tách sợi:

#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 cách là

  • Nếu stdin là nhận được đầu vào người sử dụng, có lối ra thích hợp trong chủ đề (không chấm dứt đầu vào tương tác, ra khỏi màu xanh)
  • Non-blocking đọc từ stdin (đó là hệ thống phụ thuộc)
  • Thiết lập một đường ống dẫn
  • Sử dụng một ổ cắm
+0

Đã được thử. Ứng dụng không thoát theo cách này. Chờ cho một khóa trong _locterm(). – simon

+0

Tôi đã có thể làm cho phương pháp này làm việc trong mã của tôi bằng cách có khối chuỗi đọc stdin trong ReadFile() chứ không phải là getline(). Dường như làm việc ổn cho tôi (mặc dù tôi nghĩ rằng đó là một hack khủng khiếp để lại một thread chạy như thế). Các mã có liên quan có thể được nhìn thấy bắt đầu từ dòng 28 của tập tin này: https://public.msli.com/lcs/muscle/muscle/dataio/StdinDataIO.cpp –

0

Có không có giải pháp chuẩn và thậm chí đa nền tảng để làm gián đoạn std:cin hoặc std::thread. Bạn sẽ cần sử dụng API dành riêng cho hệ điều hành trong cả hai trường hợp. Bạn có thể truy xuất xử lý dành riêng cho hệ điều hành cho một chuỗi với std::thread::native_handle()

Là một bản hack nhanh chóng và bẩn, bạn chỉ có thể detach chuỗi. Nhưng hãy lưu ý đến số thisthat.

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

Ngoài ra:

  • Không cần phải phân bổ chủ đề trên đống:

    std::thread t([]{ 
    
    }); 
    
  • return 0; là không cần thiết trong C++
  • stop = true; sẽ kích hoạt lỗi biên dịch, như stop không được khai báo trong phạm vi này
  • Nếu bạn dự định chia sẻ cờ boolean theo cách này, bạn sẽ có race condition và do đó UB
  • Có lẽ gần nhất với giải pháp "chuẩn" hoặc "nền tảng chéo" cho đầu vào không chặn có thể là ncurses (như trên * nix, và pdcurses trên Windows)
+0

Phải, chỉnh sửa quá nhiều ... Tôi đã sửa lại ví dụ compilable. – simon

+0

Tách rời không hoạt động, lối ra bị khóa bởi một khóa. – simon

+0

@simon Chính xác thì không hiệu quả? "Lối ra bị chặn bởi một khóa" nghĩa là gì? Đối với tôi, chương trình bắt đầu và thoát trong 1 giây trên cả Linux (GCC 4.9.2) và Windows (VS2013u5). Bạn sử dụng nền tảng, trình biên dịch và lib chuẩn nào? Bạn đã thử đoạn mã của tôi đúng nguyên văn chưa? – Drop

0

Nếu không có gì khác hoạt động, luôn có tùy chọn hạt nhân:

TerminateProcess(GetCurrentProcess(), 0); 

Chỉ cần chắc chắn rằng bạn đã đỏ mặt bất kỳ của bộ đệm runtime bạn quan tâm.

+0

Với tùy chọn trình biên dịch/MT, tính năng này cũng bị chặn. Chương trình sẽ không thoát. – simon

+0

Làm việc cho tôi, ngay cả với/MT. Bạn đang chạy hệ điều hành nào? –

+0

Windows 8.1 64bit – simon

0

mã này là đa luồng. trước hết, tại sao tạo một luồng mới trên heap? chỉ cần khai báo trên ngăn xếp và gọi std::thread::detach.
giây, ai đã hứa với bạn rằng stop trong ngữ cảnh này sẽ hoạt động? nó là nhiều hơn có thể là bộ xử lý lưu trữ boolean này và không bao giờ nhìn vào thực tế (nếu không một phần tối ưu hóa nó đi, hoặc thủ đoạn biên dịch khác ..). bạn cần phải làm cho nó nguyên tử:

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

được biên soạn với visual studio 2013 trên windows 7 và hoạt động như mong đợi.

+0

Đó chỉ là một mã ví dụ để hiển thị tác vụ tôi muốn thực hiện. Mã của bạn sẽ không thoát với trình chuyển đổi trình biên dịch/MT, mà tôi phải sử dụng. – simon

+0

để vấn đề của bạn không có. là chủ đề thực sự của bạn là tĩnh đến một số lớp học? –

0

này làm việc cho tôi, mặc dù đó là một chút tinh ranh:

#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

Không làm việc cho tôi. Cuộc gọi CloseHandle không trả lại. – simon

+0

Bạn có thể thử trên máy ảo mới được cài đặt không? Tôi nghĩ rằng có thể có phần mềm của bên thứ ba (chống virus, có lẽ) can thiệp. AFAIK, CloseHandle không được phép chặn trong mọi trường hợp. –

+0

Tuy nhiên, một cách tiếp cận khác, tôi giả sử, sẽ là chèn các ký tự đầu vào thông qua WriteConsoleInput để đường thoát ra khỏi tự nhiên một cách tự nhiên. Mặc dù khá lộn xộn. –

Các vấn đề liên quan