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
.