Saya terkejut mengetahui bahwa setelah 5 tahun, semua jawaban masih mengalami satu atau lebih masalah berikut:
- Fungsi selain ReadLine digunakan, menyebabkan hilangnya fungsionalitas. (Hapus / spasi mundur / tombol atas untuk masukan sebelumnya).
- Fungsi berperilaku buruk ketika dipanggil beberapa kali (memunculkan banyak utas, banyak ReadLine yang menggantung, atau perilaku yang tidak terduga).
- Fungsi bergantung pada sibuk-menunggu. Yang merupakan pemborosan yang mengerikan karena menunggu diperkirakan berjalan di mana saja dari beberapa detik hingga batas waktu, yang mungkin beberapa menit. Penantian yang sibuk yang berlangsung selama jumlah waktu seperti itu adalah menyedot sumber daya yang mengerikan, yang sangat buruk dalam skenario multithreading. Jika busy-wait diubah dengan sleep ini memiliki efek negatif pada responsivitas, meskipun saya akui bahwa ini mungkin bukan masalah besar.
Saya yakin solusi saya akan menyelesaikan masalah asli tanpa menderita salah satu masalah di atas:
class Reader {
private static Thread inputThread;
private static AutoResetEvent getInput, gotInput;
private static string input;
static Reader() {
getInput = new AutoResetEvent(false);
gotInput = new AutoResetEvent(false);
inputThread = new Thread(reader);
inputThread.IsBackground = true;
inputThread.Start();
}
private static void reader() {
while (true) {
getInput.WaitOne();
input = Console.ReadLine();
gotInput.Set();
}
}
// omit the parameter to read a line without a timeout
public static string ReadLine(int timeOutMillisecs = Timeout.Infinite) {
getInput.Set();
bool success = gotInput.WaitOne(timeOutMillisecs);
if (success)
return input;
else
throw new TimeoutException("User did not provide input within the timelimit.");
}
}
Menelepon, tentu saja, sangat mudah:
try {
Console.WriteLine("Please enter your name within the next 5 seconds.");
string name = Reader.ReadLine(5000);
Console.WriteLine("Hello, {0}!", name);
} catch (TimeoutException) {
Console.WriteLine("Sorry, you waited too long.");
}
Atau, Anda dapat menggunakan TryXX(out)
konvensi, seperti yang disarankan shmueli:
public static bool TryReadLine(out string line, int timeOutMillisecs = Timeout.Infinite) {
getInput.Set();
bool success = gotInput.WaitOne(timeOutMillisecs);
if (success)
line = input;
else
line = null;
return success;
}
Yang disebut sebagai berikut:
Console.WriteLine("Please enter your name within the next 5 seconds.");
string name;
bool success = Reader.TryReadLine(out name, 5000);
if (!success)
Console.WriteLine("Sorry, you waited too long.");
else
Console.WriteLine("Hello, {0}!", name);
Dalam kedua kasus, Anda tidak dapat menggabungkan panggilan ke Reader
dengan Console.ReadLine
panggilan biasa : jika Reader
waktu habis, akan ada ReadLine
panggilan gantung . Sebaliknya, jika Anda ingin melakukan panggilan normal (tidak berjangka waktu) ReadLine
, cukup gunakan Reader
dan hilangkan waktu tunggu, sehingga secara default ke waktu tunggu tak terbatas.
Jadi bagaimana dengan masalah dari solusi lain yang saya sebutkan?
- Seperti yang Anda lihat, ReadLine digunakan, menghindari masalah pertama.
- Fungsi ini berperilaku dengan baik saat dipanggil beberapa kali. Terlepas dari apakah waktu tunggu terjadi atau tidak, hanya satu utas latar belakang yang akan berjalan dan paling banyak hanya satu panggilan ke ReadLine yang akan aktif. Memanggil fungsi tersebut akan selalu menghasilkan masukan terbaru, atau waktu tunggu, dan pengguna tidak perlu menekan enter lebih dari sekali untuk mengirimkan masukannya.
- Dan, jelas, fungsinya tidak bergantung pada sibuk-menunggu. Sebaliknya ia menggunakan teknik multithreading yang tepat untuk mencegah pemborosan sumber daya.
Satu-satunya masalah yang saya perkirakan dengan solusi ini adalah bahwa ini tidak aman untuk thread. Namun, beberapa utas tidak dapat benar-benar meminta input pengguna pada saat yang sama, jadi sinkronisasi harus dilakukan sebelum melakukan panggilan ke sana Reader.ReadLine
.
horrible waste
, tapi tentu saja pensinyalan Anda lebih unggul. Selain itu, menggunakan satuConsole.ReadLine
panggilan pemblokiran dalam loop tak terbatas dalam ancaman kedua mencegah masalah dengan banyak panggilan seperti itu yang berkeliaran di latar belakang seperti pada solusi lain yang diberi suara tinggi, di bawah ini. Terima kasih telah membagikan kode Anda. +1Console.ReadLine()
panggilan berikutnya yang Anda lakukan. Anda berakhir dengan "hantu"ReadLine
yang harus diselesaikan terlebih dahulu.getInput
.sumber
ReadLine
Anda menelepon duduk di sana menunggu masukan. Jika Anda menyebutnya 100 kali, itu membuat 100 utas yang tidak semuanya hilang sampai Anda menekan Enter 100 kali!Akankah pendekatan ini menggunakan bantuan Console.KeyAvailable ?
sumber
KeyAvailable
hanya menunjukkan bahwa pengguna telah mulai mengetikkan input ke ReadLine, tetapi kami memerlukan acara saat menekan Enter, yang membuat ReadLine kembali. Solusi ini hanya berfungsi untuk ReadKey, yaitu mendapatkan hanya satu karakter. Karena ini tidak menyelesaikan pertanyaan sebenarnya untuk ReadLine, saya tidak dapat menggunakan solusi Anda. -1 maafIni berhasil untuk saya.
sumber
Dengan satu atau lain cara Anda memang membutuhkan utas kedua. Anda dapat menggunakan IO asinkron untuk menghindari deklarasi milik Anda sendiri:
Jika pembacaan mengembalikan data, setel acara dan utas utama Anda akan dilanjutkan, jika tidak Anda akan melanjutkan setelah waktu tunggu.
sumber
sumber
Saya pikir Anda perlu membuat utas sekunder dan polling untuk kunci di konsol. Saya tahu tidak ada cara yang dibangun untuk mencapai ini.
sumber
Saya berjuang dengan masalah ini selama 5 bulan sebelum saya menemukan solusi yang bekerja dengan sempurna dalam pengaturan perusahaan.
Masalah dengan sebagian besar solusi sejauh ini adalah mereka mengandalkan sesuatu selain Console.ReadLine (), dan Console.ReadLine () memiliki banyak keuntungan:
Solusi saya adalah sebagai berikut:
Kode sampel:
Informasi lebih lanjut tentang teknik ini, termasuk teknik yang benar untuk membatalkan utas yang menggunakan Console. ReadLine:
.NET panggilan untuk mengirim [enter] keystroke ke dalam proses saat ini, yang merupakan aplikasi konsol?
Bagaimana cara membatalkan utas lain di .NET, ketika utas tersebut menjalankan Console.ReadLine?
sumber
Memanggil Console.ReadLine () dalam delegasi buruk karena jika pengguna tidak menekan 'enter' maka panggilan itu tidak akan pernah kembali. Utas yang mengeksekusi delegasi akan diblokir hingga pengguna menekan 'enter', tanpa ada cara untuk membatalkannya.
Mengeluarkan urutan panggilan ini tidak akan berfungsi seperti yang Anda harapkan. Pertimbangkan hal berikut (menggunakan contoh kelas Console dari atas):
Pengguna membiarkan batas waktu kedaluwarsa untuk permintaan pertama, lalu memasukkan nilai untuk permintaan kedua. Baik firstName dan lastName akan berisi nilai default. Ketika pengguna menekan 'enter', panggilan ReadLine pertama akan selesai, tetapi kode telah meninggalkan panggilan itu dan pada dasarnya mengabaikan hasilnya. The kedua panggilan ReadLine akan terus memblokir, timeout akhirnya akan berakhir dan nilai yang dikembalikan lagi akan menjadi default.
BTW- Ada bug pada kode di atas. Dengan memanggil waitHandle.Close () Anda menutup acara dari bawah utas pekerja. Jika pengguna menekan 'enter' setelah waktu tunggu habis, thread pekerja akan mencoba memberi sinyal peristiwa yang menampilkan ObjectDisposedException. Pengecualian dilempar dari utas pekerja, dan jika Anda belum menyiapkan penangan pengecualian yang tidak tertangani, proses Anda akan dihentikan.
sumber
Jika Anda dalam
Main()
metode ini, Anda tidak dapat menggunakanawait
, jadi Anda harus menggunakanTask.WaitAny()
:Namun, C # 7.1 memperkenalkan kemungkinan untuk membuat
Main()
metode asinkron , jadi lebih baik untuk menggunakanTask.WhenAny()
versi tersebut setiap kali Anda memiliki opsi itu:sumber
Saya mungkin terlalu banyak membaca pertanyaan, tetapi saya berasumsi menunggu akan mirip dengan menu boot di mana ia menunggu 15 detik kecuali Anda menekan tombol. Anda dapat menggunakan (1) fungsi pemblokiran atau (2) Anda dapat menggunakan utas, acara, dan pengatur waktu. Acara tersebut akan bertindak sebagai 'lanjutan' dan akan memblokir hingga pengatur waktu berakhir atau tombol ditekan.
Pseudo-code untuk (1) adalah:
sumber
Sayangnya saya tidak dapat mengomentari kiriman Gulzar, tetapi berikut adalah contoh yang lebih lengkap:
sumber
EDIT : perbaiki masalah dengan meminta pekerjaan yang sebenarnya dilakukan dalam proses terpisah dan menghentikan proses itu jika waktu habis. Lihat detailnya di bawah. Wah!
Coba saja ini dan sepertinya bekerja dengan baik. Rekan kerja saya memiliki versi yang menggunakan objek Thread, tetapi saya menemukan metode BeginInvoke () dari tipe delegasi menjadi sedikit lebih elegan.
Proyek ReadLine.exe adalah proyek yang sangat sederhana yang memiliki satu kelas yang terlihat seperti ini:
sumber
Console.ReadLine()
yang menghalangi dan akan tahan masukan atas permintaan berikutnya. Jawaban yang diterima cukup dekat, tetapi masih memiliki keterbatasan.ReadLine()
dalam program Anda setelah memanggil yang ini. Lihat apa yang terjadi. Anda harus menekan tombol kembali DUA KALI untuk melakukannya karena sifat utas tunggalConsole
. Itu. Tidak. Kerja..NET 4 membuat ini sangat sederhana menggunakan Tasks.
Pertama, buat helper Anda:
Kedua, jalankan dengan tugas dan tunggu:
Tidak ada upaya untuk membuat ulang fungsionalitas ReadLine atau melakukan peretasan berbahaya lainnya untuk membuatnya berfungsi. Tugas memungkinkan kami menyelesaikan pertanyaan dengan cara yang sangat alami.
sumber
Seolah-olah belum ada cukup jawaban di sini: 0), berikut ini merangkum menjadi solusi metode statis @ kwl di atas (yang pertama).
Pemakaian
sumber
Contoh threading sederhana untuk mengatasi ini
atau string statis di bagian atas untuk mendapatkan seluruh baris.
sumber
Saya kasus saya ini berfungsi dengan baik:
sumber
Bukankah ini bagus dan pendek?
sumber
Ini adalah contoh lengkap dari solusi Glen Slayden. Saya dengan senang hati membuat ini saat membuat kasus uji untuk masalah lain. Ini menggunakan asynchronous I / O dan acara reset manual.
sumber
Kode saya sepenuhnya didasarkan pada jawaban teman @JSQuareD
Tetapi saya perlu menggunakan
Stopwatch
to timer karena ketika saya menyelesaikan programConsole.ReadKey()
itu masih menungguConsole.ReadLine()
dan menghasilkan perilaku yang tidak terduga.Ini BERFUNGSI SEMPURNA bagi saya. Mempertahankan Konsol asli.ReadLine ()
sumber
Cara murah lainnya untuk mendapatkan utas kedua adalah membungkusnya dengan seorang delegasi.
sumber
Contoh implementasi postingan Eric di atas. Contoh khusus ini digunakan untuk membaca informasi yang diteruskan ke aplikasi konsol melalui pipa:
sumber
Perhatikan bahwa jika Anda menggunakan rute "Console.ReadKey", Anda kehilangan beberapa fitur keren ReadLine, yaitu:
Untuk menambahkan batas waktu, ubah loop sementara agar sesuai.
sumber
Tolong jangan membenci saya karena menambahkan solusi lain ke kebanyakan jawaban yang ada! Ini berfungsi untuk Console.ReadKey (), tetapi dapat dengan mudah dimodifikasi untuk bekerja dengan ReadLine (), dll.
Karena metode "Console.Read" memblokir, aliran StdIn perlu " mendorong " aliran untuk membatalkan pembacaan.
Memanggil sintaks:
Kode:
sumber
Berikut adalah solusi yang digunakan
Console.KeyAvailable
. Ini adalah panggilan pemblokiran, tetapi seharusnya cukup sepele untuk memanggilnya secara asinkron melalui TPL jika diinginkan. Saya menggunakan mekanisme pembatalan standar untuk membuatnya mudah disambungkan dengan Pola Asinkron Tugas dan semua hal bagus itu.Ada beberapa kekurangannya.
ReadLine
menyediakan (scrolling panah atas / bawah, dll.).sumber
Berakhir di sini karena pertanyaan duplikat telah diajukan. Saya datang dengan solusi berikut yang terlihat langsung. Saya yakin ada beberapa kekurangan yang saya lewatkan.
sumber
Saya sampai pada jawaban ini dan akhirnya melakukan:
sumber
Contoh sederhana menggunakan
Console.KeyAvailable
:sumber
Kode yang jauh lebih kontemporer dan berbasis tugas akan terlihat seperti ini:
sumber
Saya memiliki situasi unik karena memiliki Aplikasi Windows (Layanan Windows). Saat menjalankan program secara interaktif
Environment.IsInteractive
(VS Debugger atau dari cmd.exe), saya menggunakan AttachConsole / AllocConsole untuk mendapatkan stdin / stdout saya. Agar proses tidak berakhir saat pekerjaan sedang dilakukan, UI Thread memanggilConsole.ReadKey(false)
. Saya ingin membatalkan penantian yang dilakukan utas UI dari utas lain, jadi saya membuat modifikasi pada solusi oleh @JSquaredD.sumber