কনসোল-টাইমআউটকে কীভাবে যুক্ত করবেন? রিডলাইন ()?


122

আমার কাছে একটি কনসোল অ্যাপ্লিকেশন রয়েছে যাতে আমি ব্যবহারকারীকে প্রম্পটের প্রতিক্রিয়া জানাতে এক্স সেকেন্ড দিতে চাই । নির্দিষ্ট সময়ের পরে যদি কোনও ইনপুট তৈরি না করা হয় তবে প্রোগ্রাম লজিকটি চালিয়ে যাওয়া উচিত। আমরা ধরে নিলাম একটি সময়সীমা মানে ফাঁকা প্রতিক্রিয়া।

এটি কাছে যাওয়ার সহজ সরল উপায় কী?

উত্তর:


112

আমি অবাক হয়ে জানতে পেরেছি যে 5 বছর পরে, সমস্ত উত্তর এখনও নীচের এক বা একাধিক সমস্যায় ভুগছে:

  • রিডলাইন ব্যতীত একটি ফাংশন ব্যবহৃত হয়, যার ফলে কার্যকারিতা হ্রাস পায়। (পূর্ববর্তী ইনপুটটির জন্য / ব্যাকস্পেস / আপ-কী মুছুন)।
  • একাধিকবার আহ্বান করা হলে ফাংশনটি খারাপভাবে আচরণ করে (একাধিক থ্রেড তৈরি করে, অনেকগুলি হ'ল রিডলাইন ঝুলিয়ে থাকে বা অন্যথায় অপ্রত্যাশিত আচরণ করে)।
  • ফাংশন একটি ব্যস্ততার জন্য অপেক্ষা করে। অপেক্ষাটি বেশ সেকেন্ড থেকে সময়সীমা পর্যন্ত যে কোনও জায়গায় চলবে বলে আশা করা হওয়ায় এটি একটি ভয়ঙ্কর বর্জ্য, যা একাধিক মিনিট হতে পারে। একটি ব্যস্ততা-অপেক্ষা যা এই সময়ের জন্য প্রচুর পরিমাণে সঞ্চালিত হয় হ'ল সংস্থানগুলির এক ভয়াবহ স্তন্যপান, যা মাল্টিথ্রেডিং দৃশ্যে বিশেষত খারাপ। ব্যস্ততা-অপেক্ষা যদি ঘুমের সাথে সংশোধন করা হয় তবে এটি প্রতিক্রিয়াশীলতার উপর নেতিবাচক প্রভাব ফেলবে, যদিও আমি স্বীকার করি যে এটি সম্ভবত কোনও বিশাল সমস্যা নয়।

আমি বিশ্বাস করি যে আমার সমাধান উপরের সমস্যার কোনওটি না ভেবে মূল সমস্যাটি সমাধান করবে:

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

কল করা অবশ্যই খুব সহজ:

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

বিকল্প হিসাবে, আপনি TryXX(out)কনভেনশনটি ব্যবহার করতে পারেন , যেমন শমুয়েলই পরামর্শ দিয়েছেন:

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

যা নিম্নলিখিত হিসাবে বলা হয়:

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

উভয় ক্ষেত্রেই আপনি Readerসাধারণ Console.ReadLineকলগুলির সাথে কলগুলিতে মিশ্রণ করতে পারবেন না : যদি Readerসময় শেষ হয়ে যায় তবে একটি হ্যাঙ্গিং ReadLineকল আসবে । পরিবর্তে, আপনি যদি একটি সাধারণ (সময়সীমার) ReadLineকল করতে চান তবে কেবল Readerসময়সীমাটি ব্যবহার করুন এবং বাদ দিন, যাতে এটি অসীম সময়সীমার ডিফল্ট হয়।

তাহলে আমি অন্যান্য সমাধানগুলির উল্লিখিত সমস্যাগুলির সম্পর্কে কী করব?

  • যেমন আপনি দেখতে পাচ্ছেন, রিডলাইন প্রথম সমস্যাটিকে এড়িয়ে চলে।
  • একাধিকবার আহ্বান করা হলে ফাংশনটি সঠিকভাবে আচরণ করে। একটি সময়সীমা ঘটে কিনা তা নির্বিশেষে, কেবলমাত্র একটি মাত্র পটভূমি থ্রেড চলতে থাকবে এবং কেবলমাত্র রিডলাইনে কেবলমাত্র একটি কল সক্রিয় থাকবে। ফাংশনটি কল করার ফলে সর্বদা সর্বশেষতম ইনপুট বা সময়সীমার ফলাফল আসবে এবং ব্যবহারকারীকে তার ইনপুট জমা দেওয়ার জন্য একাধিকবার এন্টার চাপতে হবে না।
  • এবং, স্পষ্টতই, ফাংশনটি ব্যস্ততার জন্য অপেক্ষা করে না। পরিবর্তে এটি সম্পদ নষ্ট হওয়া রোধ করতে যথাযথ মাল্টিথ্রেডিং কৌশল ব্যবহার করে।

এই সমাধানটি নিয়ে আমি কেবলমাত্র সমস্যার আগে থেকেই বলেছি এটি থ্রেড-নিরাপদ নয়। তবে একাধিক থ্রেড একই সময়ে ব্যবহারকারীকে ইনপুট চেয়ে নিতে পারে না, সুতরাং যে Reader.ReadLineকোনও উপায়ে কল করার আগে সুসংগত হওয়া উচিত ।


1
এই কোডটি অনুসরণ করে আমি একটি নালরফেরানএক্সেপশন পেয়েছি। আমি মনে করি যে একবার স্বয়ংক্রিয় ইভেন্টগুলি তৈরি হওয়ার পরে আমি থ্রেডটি শুরু করে ফিক্স করতে পারতাম।
অগস্টো পেদ্রাজা

1
@ জেএসকিয়ারডি আমি মনে করি না যে ঘুম (200 মিমি) নিয়ে একটি ব্যস্ততা-অপেক্ষা এটি অনেক বেশি horrible waste, তবে অবশ্যই আপনার সিগন্যালিং উচ্চতর। এছাড়াও, Console.ReadLineদ্বিতীয় হুমকিতে অসীম-লুপে একটি ব্লকিং কল ব্যবহার করা নীচে অন্যান্য সমাধানের মতো প্রচুর কলগুলিকে ব্যাকগ্রাউন্ডে ঝুলিয়ে রাখা সমস্যাগুলি প্রতিরোধ করে। আপনার কোড ভাগ করে নেওয়ার জন্য আপনাকে ধন্যবাদ। +1
রোল্যান্ড

2
আপনি যদি সময়মতো ইনপুট করতে ব্যর্থ হন তবে এই পদ্ধতিটি Console.ReadLine()আপনার পরে করা প্রথম কলটি মনে হচ্ছে । আপনি একটি "ভুত" দিয়ে শেষ করেন যা ReadLineপ্রথমে সম্পূর্ণ করা দরকার।
ডেরেক

1
@ ডেরেক দুর্ভাগ্যক্রমে, আপনি এই পদ্ধতিটি সাধারণ রিডলাইন কলগুলির সাথে মিশতে পারবেন না, সমস্ত কল অবশ্যই পাঠকের মাধ্যমে করা উচিত। এই সমস্যার সমাধানটি হ'ল পাঠকটিতে এথোড যুক্ত করা যা সময়সীমা ছাড়াই গোল্ড ইনপুটটির জন্য অপেক্ষা করে। আমি বর্তমানে মোবাইলে রয়েছি, তাই আমি খুব সহজেই উত্তরে এটি যুক্ত করতে পারি না।
জেএসকিয়ার

1
আমি প্রয়োজন দেখতে পাচ্ছি না getInput
সিলভল্লি

33
string ReadLine(int timeoutms)
{
    ReadLineDelegate d = Console.ReadLine;
    IAsyncResult result = d.BeginInvoke(null, null);
    result.AsyncWaitHandle.WaitOne(timeoutms);//timeout e.g. 15000 for 15 secs
    if (result.IsCompleted)
    {
        string resultstr = d.EndInvoke(result);
        Console.WriteLine("Read: " + resultstr);
        return resultstr;
    }
    else
    {
        Console.WriteLine("Timed out!");
        throw new TimedoutException("Timed Out!");
    }
}

delegate string ReadLineDelegate();

2
কেন জানি এটিকে ভোট দেওয়া হয়নি - এটি একেবারে নির্দোষভাবে কাজ করে। অন্যান্য সমাধানগুলির মধ্যে অনেকগুলি "রিডকি ()" জড়িত, যা সঠিকভাবে কাজ করে না: এর অর্থ আপনি রিডলাইন () এর সমস্ত শক্তি হারাবেন, যেমন পূর্বে টাইপিত কমান্ডটি পেতে "আপ" কী টিপুন, ব্যাকস্পেস এবং নির্দেশক তীরচিহ্নগুলি, ইত্যাদি
কন্ট্যাঙ্গো

9
@ গ্রাভিটাস: এটি কাজ করে না। ঠিক আছে, এটি একবার কাজ করে। তবে ReadLineআপনি যে প্রত্যেকে কল করেন সেখানে ইনপুটটির অপেক্ষায় বসে আছেন। আপনি যদি এটি 100 বার কল করেন তবে এটি 100 টি থ্রেড তৈরি করে যা 100 বার প্রবেশ না করে যতক্ষণ না সব দূর হয় না!
গ্যাবে

2
সতর্ক থাকুন। এই সমাধানটি ঝরঝরে দেখা যাচ্ছে তবে আমি অসম্পূর্ণ কলগুলি ঝুলিয়ে রেখেছি with তাই বারবার বলা ভাল না।
টম মাকিন

@ গ্যাবে, শকিনফ্রি: সমাধানের জন্য একাধিক কল বিবেচনা করা হয়নি তবে সময়সীমা সহ কেবলমাত্র একটি অ্যাসিঙ্ক কল। আমি অনুমান করি যে এটি কনসোলে 10 টি বার্তা মুদ্রিত হওয়া এবং তারপরে তাদের ক্রম অনুসারে প্রতিটি ক্রমে ইনপুট প্রবেশ করানো বিভ্রান্তিকর হবে। হ্যাং কলগুলির জন্য নিখরচায়, আপনি কি টাইমআউটএক্সপশন লাইনে মন্তব্য করার চেষ্টা করতে এবং নাল / ফাঁকা স্ট্রিংটি ফেরত দিতে পারেন?
জিপি

নাহ ... সমস্যাটি কনসোল R রিডলাইন এখনও থ্রেডপুলের থ্রেডটি অবরুদ্ধ করছে যা কনসোল চলছে Read রিডলাইন পদ্ধতিটি ReadLineDelegate থেকে।
জিপি

27

এই পন্থাটি কনসোল.কিয়েআলভ্যালিবল সহায়তা ব্যবহার করে ?

class Sample 
{
    public static void Main() 
    {
    ConsoleKeyInfo cki = new ConsoleKeyInfo();

    do {
        Console.WriteLine("\nPress a key to display; press the 'x' key to quit.");

// Your code could perform some useful task in the following loop. However, 
// for the sake of this example we'll merely pause for a quarter second.

        while (Console.KeyAvailable == false)
            Thread.Sleep(250); // Loop until input is entered.
        cki = Console.ReadKey(true);
        Console.WriteLine("You pressed the '{0}' key.", cki.Key);
        } while(cki.Key != ConsoleKey.X);
    }
}

এটি সত্য, ওপি একটি ব্লকিং কল চায় বলে মনে হচ্ছে, যদিও আমি কিছুটা চিন্তাভাবনা থেকে কাঁপছি ... এটি সম্ভবত আরও ভাল সমাধান।
জিওচেট

আমি নিশ্চিত আপনি এটি দেখেছেন। তাড়াতাড়ি গুগল সোশ্যাল.এমএসএনএন.মাইক্রোসফট.ফর্মস
গুলজার নাজিম

যদি ব্যবহারকারী কিছু না করে তবে এই "টাইমআউট "টি আমি দেখতে পাচ্ছি না। কীগুলি টিপানো এবং অন্যান্য যুক্তি অব্যাহত না হওয়া অবধি পটভূমিতে লজিকের সম্পাদন চালিয়ে যাওয়া এই সমস্ত কিছুই করবে।
mphair

সত্য, এটি ঠিক করা দরকার। লুপের সময়সীমাটি যুক্ত করা যথেষ্ট সহজ।
জোনাথন অ্যালেন

KeyAvailableকেবলমাত্র ইঙ্গিত দেয় যে ব্যবহারকারীটি রিডলাইনটিতে ইনপুট টাইপ করতে শুরু করেছে, তবে আমাদের এন্টার টিপে একটি ইভেন্টের প্রয়োজন যা রিডলাইনকে ফিরিয়ে দেয়। এই সমাধানটি কেবল ReadKey এর জন্য কাজ করে, কেবলমাত্র একটি অক্ষর পাওয়ার জন্য। যেহেতু এটি রিডলাইনটির আসল প্রশ্নটি সমাধান করে না, তাই আমি আপনার সমাধানটি ব্যবহার করতে পারি না। -1 দুঃখিত
রোল্যান্ড

13

এটি আমার পক্ষে কাজ করেছে।

ConsoleKeyInfo k = new ConsoleKeyInfo();
Console.WriteLine("Press any key in the next 5 seconds.");
for (int cnt = 5; cnt > 0; cnt--)
  {
    if (Console.KeyAvailable)
      {
        k = Console.ReadKey();
        break;
      }
    else
     {
       Console.WriteLine(cnt.ToString());
       System.Threading.Thread.Sleep(1000);
     }
 }
Console.WriteLine("The key pressed was " + k.Key);

4
আমি মনে করি এটি ইতিমধ্যে নির্মিত সরঞ্জামগুলি ব্যবহার করে সেরা এবং সহজ সমাধান। দারূন কাজ!
ভিপ্পি

2
সুন্দর! সরলতা আসলে চূড়ান্ত পরিশীলিত। অভিনন্দন!
ব্রুনোসালভিনো

10

এক উপায় বা অন্য আপনার দ্বিতীয় থ্রেডের প্রয়োজন। আপনার নিজের ঘোষণা এড়াতে আপনি অ্যাসিঙ্ক্রোনাস আইও ব্যবহার করতে পারেন:

  • একটি ম্যানুয়ালসেটসেন্ট ঘোষণা করুন, এটিকে "evt" বলুন
  • ইনপুট স্ট্রিম পেতে সিস্টেম.কনসোল.অপেনস্ট্যান্ডার্ডডিনপুট কল করুন। একটি কলব্যাক পদ্ধতি সুনির্দিষ্ট করুন যা এর ডেটা সংরক্ষণ করবে এবং প্রাক্কলিত সেট করবে।
  • অ্যাসিঙ্ক্রোনাস রিড অপারেশন শুরু করতে সেই স্ট্রিমের বিগিনিডার পদ্ধতিতে কল করুন
  • তারপরে একটি ম্যানুয়ালসেটসেন্টে সময়সীমা অপেক্ষা করুন
  • অপেক্ষার সময় শেষ হয়ে গেলে, পড়াটি বাতিল করুন

যদি পঠিত ডেটা ফেরত আসে, ইভেন্টটি সেট করুন এবং আপনার মূল থ্রেড চলতে থাকবে, অন্যথায় আপনি সময়সীমা পেরিয়ে গেলেও চালিয়ে যাবেন।


এটি গ্রহণযোগ্য সমাধান যা কমবেশি তা করে।
রোল্যান্ড

9
// Wait for 'Enter' to be pressed or 5 seconds to elapse
using (Stream s = Console.OpenStandardInput())
{
    ManualResetEvent stop_waiting = new ManualResetEvent(false);
    s.BeginRead(new Byte[1], 0, 1, ar => stop_waiting.Set(), null);

    // ...do anything else, or simply...

    stop_waiting.WaitOne(5000);
    // If desired, other threads could also set 'stop_waiting' 
    // Disposing the stream cancels the async read operation. It can be
    // re-opened if needed.
}

8

আমি মনে করি আপনার কনসোলে একটি কীটির জন্য একটি দ্বিতীয় থ্রেড এবং পোল তৈরি করতে হবে। আমি জানি এটি সম্পন্ন করার জন্য কোনও বিল্টই নেই।


হ্যাঁ যদি আপনার কীগুলির জন্য দ্বিতীয় থ্রেড পোলিং থাকে এবং আপনার অ্যাপটি অপেক্ষা করতে বসে এটি বন্ধ থাকে, সেই কী পোলিং থ্রেডটি কেবল সেখানে বসে থাকবে, অপেক্ষা করে চিরকাল থাকবে।
কেলি এলটন

প্রকৃতপক্ষে: হয় দ্বিতীয় থ্রেড, বা "বিগিনিভোকে" (যা পর্দার আড়ালে থ্রেড ব্যবহার করে - @ জিপি থেকে উত্তর দেখুন) সহ একটি প্রতিনিধি।
কনটাঙ্গো

@ কেলটন52, আপনি যদি টাস্ক ম্যানেজারে প্রক্রিয়াটি শেষ করেন তবে দ্বিতীয় থ্রেডটি কি প্রস্থান করবে?
অ্যারলেন বেলার

6

এন্টারপ্রাইজ সেটিংয়ে পুরোপুরি কাজ করে এমন কোনও সমাধান খুঁজে পাওয়ার আগে আমি এই সমস্যাটির সাথে 5 মাস লড়াই করেছি।

এখনও অবধি বেশিরভাগ সমাধানের সমস্যা হ'ল তারা কনসোল.আরডলাইন () এবং কনসোল.আডলাইন () ব্যতীত অন্য কোনও কিছুর উপর নির্ভর করে:

  • মোছা, ব্যাকস্পেস, তীর কী ইত্যাদি জন্য সমর্থন
  • "আপ" কী টিপতে এবং শেষ কমান্ডটির পুনরাবৃত্তি করার ক্ষমতা (যদি আপনি একটি ব্যাকগ্রাউন্ড ডিবাগিং কনসোল প্রয়োগ করেন যা প্রচুর ব্যবহার পায়) তবে এটি খুব কার্যকর হবে।

আমার সমাধানটি নিম্নরূপ:

  1. কনসোল.ইডলাইন () ব্যবহার করে ব্যবহারকারীর ইনপুট পরিচালনা করতে একটি পৃথক থ্রেড স্প্যান করুন।
  2. সময়পর্ব, অবরোধ মুক্ত Console.ReadLine (), বর্তমান কনসোল উইন্ডোতে একটি [Enter] কী পাঠানোর ব্যবহার করে পরে http://inputsimulator.codeplex.com/

কোডের উদাহরণ:

 InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);

কনসোল ব্যবহার করে এমন থ্রেড বাতিল করতে সঠিক কৌশল সহ এই কৌশল সম্পর্কিত আরও তথ্য .প্রেডলাইন:

.NET কলটি বর্তমান প্রক্রিয়ায় কী-স্ট্রোক পাঠাতে [enter] প্রেরণ করবে, কোন কনসোল অ্যাপ্লিকেশন?

.NET- এ অন্য থ্রেড কীভাবে বাতিল করা যায়, যখন বলা হয় যে থ্রেড কনসোল চালাচ্ছে? রিডলাইন?


5

কনসোলকে কল করুন R রিডলাইন () ডেলিগেটে খারাপ কারণ ব্যবহারকারী যদি 'প্রবেশ' না করে তবে সেই কলটি আর ফিরে আসবে না। প্রতিনিধি সম্পাদনকারী থ্রেডটি বাতিল করা ছাড়া কোনও উপায় ছাড়াই ব্যবহারকারী 'প্রবেশ' না করা পর্যন্ত অবরুদ্ধ থাকবে।

এই কলগুলির একটি ক্রম জারি করা আপনার আচরণ মতো আচরণ করবে না। নিম্নলিখিতটি বিবেচনা করুন (উপরের উদাহরণটি কনসোল শ্রেণীর ব্যবহার করে):

System.Console.WriteLine("Enter your first name [John]:");

string firstName = Console.ReadLine(5, "John");

System.Console.WriteLine("Enter your last name [Doe]:");

string lastName = Console.ReadLine(5, "Doe");

ব্যবহারকারী প্রথম প্রম্পটের জন্য সময়সীমা শেষ হতে দেয়, তারপরে দ্বিতীয় প্রম্পটের জন্য একটি মান প্রবেশ করে। ফার্সনাম এবং লাস্টনাম উভয়টিতেই ডিফল্ট মান থাকবে। যখন ব্যবহারকারী 'প্রবেশ' হিট করে, তখন প্রথম রিডলাইন কলটি সম্পূর্ণ হবে, তবে কোডটি সেই কলটিকে ত্যাগ করেছে এবং ফলাফলটি মূলত ফেলে দিয়েছে ed দ্বিতীয় ReadLine কল ব্লক করতে চালিয়ে যাবে, মেয়াদ শেষ হওয়ার সময়সীমা শেষ পর্যন্ত মেয়াদ শেষ হবে এবং ফিরে মান আবার ডিফল্ট হবে।

বিটিডাব্লু- উপরের কোডটিতে একটি বাগ রয়েছে। ওয়েটহ্যান্ডল.ক্লোজ () কল করে আপনি কর্মীর থ্রেডের আওতায় ইভেন্টটি বন্ধ করে দিয়েছেন। সময়সীমা শেষ হওয়ার পরে যদি ব্যবহারকারী 'প্রবেশ' হিট করে তবে কর্মী থ্রেড ইভেন্টটিকে সংকেত দেওয়ার চেষ্টা করবে যা একটি অবজেক্ট ডিসপোজড এক্সসেপশন ছুড়ে দেয়। কর্মী থ্রেড থেকে ব্যতিক্রম নিক্ষেপ করা হয়, এবং আপনি যদি একটি অযথিত ব্যতিক্রম হ্যান্ডলার সেটআপ না করেন তবে আপনার প্রক্রিয়াটি সমাপ্ত হবে।


1
আপনার পোস্টে "উপরে" শব্দটি দ্বিধাগ্রস্ত এবং বিভ্রান্তিকর। আপনি যদি অন্য একটি উত্তর উল্লেখ করছেন, আপনার সেই উত্তরের একটি সঠিক লিঙ্ক তৈরি করা উচিত।
bzlm

5

আপনি যদি Main()পদ্ধতিতে থাকেন তবে আপনি ব্যবহার করতে পারবেন না await, তাই আপনাকে ব্যবহার করতে হবে Task.WaitAny():

var task = Task.Factory.StartNew(Console.ReadLine);
var result = Task.WaitAny(new Task[] { task }, TimeSpan.FromSeconds(5)) == 0
    ? task.Result : string.Empty;

তবে, সি # 7.1 অ্যাসিঙ্ক Main()পদ্ধতি তৈরি করার সম্ভাবনাটি প্রবর্তন করে , তাই Task.WhenAny()যখনই আপনার কাছে এই বিকল্পটি থাকে তখন সংস্করণটি ব্যবহার করা ভাল :

var task = Task.Factory.StartNew(Console.ReadLine);
var completedTask = await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(5)));
var result = object.ReferenceEquals(task, completedTask) ? task.Result : string.Empty;

4

আমি প্রশ্নটিতে খুব বেশি পড়তে পারি, তবে আমি ধরে নিচ্ছি যে অপেক্ষাটি বুট মেনুতে সমান হবে যেখানে আপনি কোনও কী টিপ না করলে এটি 15 সেকেন্ড অপেক্ষা করে। আপনি হয় (1) ব্লকিং ফাংশন বা (2) আপনি কোনও থ্রেড, একটি ইভেন্ট এবং একটি টাইমার ব্যবহার করতে পারেন। ইভেন্টটি 'চালিয়ে যাওয়া' হিসাবে কাজ করবে এবং টাইমারটির মেয়াদ শেষ না হওয়া বা কোনও কী টিপানো অবধি অবরুদ্ধ হবে।

(1) এর সিউডো কোডটি হ'ল:

// Get configurable wait time
TimeSpan waitTime = TimeSpan.FromSeconds(15.0);
int configWaitTimeSec;
if (int.TryParse(ConfigManager.AppSetting["DefaultWaitTime"], out configWaitTimeSec))
    waitTime = TimeSpan.FromSeconds(configWaitTimeSec);

bool keyPressed = false;
DateTime expireTime = DateTime.Now + waitTime;

// Timer and key processor
ConsoleKeyInfo cki;
// EDIT: adding a missing ! below
while (!keyPressed && (DateTime.Now < expireTime))
{
    if (Console.KeyAvailable)
    {
        cki = Console.ReadKey(true);
        // TODO: Process key
        keyPressed = true;
    }
    Thread.Sleep(10);
}

2

দুর্ভাগ্যক্রমে গুলজারের পোস্টে আমি মন্তব্য করতে পারি না, তবে এর পূর্ণ উদাহরণ এখানে:

            while (Console.KeyAvailable == false)
            {
                Thread.Sleep(250);
                i++;
                if (i > 3)
                    throw new Exception("Timedout waiting for input.");
            }
            input = Console.ReadLine();

দ্রষ্টব্য আপনি যদি কনসোলটি দৃশ্যমান না হয় (?) অথবা ইনপুট কোনও ফাইল থেকে নির্দেশিত হয় তবে আপনি কনসোল.ইন.পিক () ব্যবহার করতে পারেন।
জেমি কিটসন

2

সম্পাদনা : প্রকৃত কাজটি একটি পৃথক প্রক্রিয়াতে সম্পন্ন করে সমস্যাটি সমাধান করা হয়েছে এবং সময়টি যদি শেষ হয় তবে প্রক্রিয়াটি মেরে ফেলুন। বিস্তারিত জানার জন্য নীচে দেখুন। রক্ষে!

এই মাত্র একটি রান দিয়েছেন এবং মনে হচ্ছে এটি দুর্দান্তভাবে কাজ করবে। আমার সহকর্মীর একটি সংস্করণ ছিল যা থ্রেড অবজেক্ট ব্যবহার করেছিল, তবে আমি ডেলিগেট ধরণের বিগিনিউভোক () পদ্ধতিটি আরও কিছুটা মার্জিত বলে মনে করি।

namespace TimedReadLine
{
   public static class Console
   {
      private delegate string ReadLineInvoker();

      public static string ReadLine(int timeout)
      {
         return ReadLine(timeout, null);
      }

      public static string ReadLine(int timeout, string @default)
      {
         using (var process = new System.Diagnostics.Process
         {
            StartInfo =
            {
               FileName = "ReadLine.exe",
               RedirectStandardOutput = true,
               UseShellExecute = false
            }
         })
         {
            process.Start();

            var rli = new ReadLineInvoker(process.StandardOutput.ReadLine);
            var iar = rli.BeginInvoke(null, null);

            if (!iar.AsyncWaitHandle.WaitOne(new System.TimeSpan(0, 0, timeout)))
            {
               process.Kill();
               return @default;
            }

            return rli.EndInvoke(iar);
         }
      }
   }
}

রিডলাইন.এক্সই প্রকল্পটি একটি খুব সাধারণ প্রকল্প যার একটি শ্রেণি রয়েছে যা দেখতে এরকম দেখাচ্ছে:

namespace ReadLine
{
   internal static class Program
   {
      private static void Main()
      {
         System.Console.WriteLine(System.Console.ReadLine());
      }
   }
}

2
একটি টাইমড রিডলাইন () কেবলমাত্র একটি অতিরিক্ত ওভারকিলের মতো শোনাচ্ছে এমন কোনও নতুন প্রক্রিয়াতে পৃথক এক্সিকিউটেবলকে ডাকে। আপনি মূলত একটি রিডলাইন () বাতিল করতে না পারার সমস্যাটি সমাধান করছেন - থ্রেডটি ব্লক করে পুরো প্রক্রিয়াটি সেটআপ করে এবং পরিবর্তে ছিন্ন করে।
bzlm

তারপরে এটি মাইক্রোসফ্টকে বলুন, যিনি আমাদের এই অবস্থানে রেখেছেন।
জেসি সি স্লিকার 14

মাইক্রোসফ্ট আপনাকে সেই অবস্থাতে রাখেনি। অন্য কয়েকটি উত্তর দেখুন যা কয়েকটি লাইনে একই কাজ করে। আমি মনে করি উপরের কোডটি কিছুটা পুরষ্কার পাওয়া উচিত - তবে আপনি যে ধরণের পছন্দ করেন তা নয় :)
কনটাঙ্গো

1
না, অন্যান্য উত্তরগুলির মধ্যে ওপি যা চেয়েছিল ঠিক তেমন করে নি। এগুলির মধ্যে সমস্ত স্ট্যান্ডার্ড ইনপুট রুটিনের বৈশিষ্ট্যগুলি হারাতে পারে বা সমস্ত অনুরোধে এই সত্যটি স্তব্ধ হয়ে যায়Console.ReadLine() হয় ব্লক এবং পরবর্তী অনুরোধে ইনপুট আপ রাখা হবে। গৃহীত উত্তর মোটামুটি কাছাকাছি, তবে এখনও সীমাবদ্ধতা আছে।
জেসি সি স্লিকার

1
উম, না, তাই না। ইনপুট বাফারটি এখনও অবরুদ্ধ করে (প্রোগ্রামটি না করলেও)। এটি নিজের জন্য চেষ্টা করে দেখুন: কয়েকটি অক্ষর লিখুন তবে এন্টারটি চাপবেন না। সময়সীমা শেষ হোক। কলারে ব্যতিক্রম ক্যাপচার করুন। তারপরে ReadLine()এটিকে কল করার পরে আপনার প্রোগ্রামে আরও একটি রয়েছে। কি হয় দেখুন। একক থ্রেডযুক্ত প্রকৃতির কারণে এটি পেতে আপনাকে ফিরে পেতে TWICE টি চাপতে হবে Console। এটা তোলে। না। ওয়ার্ক।
জেসি সি স্লিকার 13

2

.NET 4 কার্যগুলি ব্যবহার করে এটি অবিশ্বাস্যভাবে সহজ করে তোলে।

প্রথমে আপনার সহায়ক তৈরি করুন:

   Private Function AskUser() As String
      Console.Write("Answer my question: ")
      Return Console.ReadLine()
   End Function

দ্বিতীয়ত, একটি কার্য সম্পাদন করুন এবং অপেক্ষা করুন:

      Dim askTask As Task(Of String) = New TaskFactory().StartNew(Function() AskUser())
      askTask.Wait(TimeSpan.FromSeconds(30))
      If Not askTask.IsCompleted Then
         Console.WriteLine("User failed to respond.")
      Else
         Console.WriteLine(String.Format("You responded, '{0}'.", askTask.Result))
      End If

এই কাজটি পাওয়ার জন্য রিডলাইন কার্যকারিতা পুনরায় তৈরি করার বা অন্য বিপদজনক হ্যাক সম্পাদনের চেষ্টা করার দরকার নেই। কার্যগুলি আমাদের খুব প্রাকৃতিক উপায়ে প্রশ্নটি সমাধান করতে দেয়।


2

যেমন এখানে ইতিমধ্যে যথেষ্ট উত্তর নেই: 0), নীচে নীচে একটি স্ট্যাটিক পদ্ধতি @ কেডাব্লুএল এর সমাধান (প্রথমটি) into

    public static string ConsoleReadLineWithTimeout(TimeSpan timeout)
    {
        Task<string> task = Task.Factory.StartNew(Console.ReadLine);

        string result = Task.WaitAny(new Task[] { task }, timeout) == 0
            ? task.Result 
            : string.Empty;
        return result;
    }

ব্যবহার

    static void Main()
    {
        Console.WriteLine("howdy");
        string result = ConsoleReadLineWithTimeout(TimeSpan.FromSeconds(8.5));
        Console.WriteLine("bye");
    }

1

এটি সমাধানের জন্য সরল থ্রেডিং উদাহরণ

Thread readKeyThread = new Thread(ReadKeyMethod);
static ConsoleKeyInfo cki = null;

void Main()
{
    readKeyThread.Start();
    bool keyEntered = false;
    for(int ii = 0; ii < 10; ii++)
    {
        Thread.Sleep(1000);
        if(readKeyThread.ThreadState == ThreadState.Stopped)
            keyEntered = true;
    }
    if(keyEntered)
    { //do your stuff for a key entered
    }
}

void ReadKeyMethod()
{
    cki = Console.ReadKey();
}

বা সম্পূর্ণ লাইন পাওয়ার জন্য একটি স্ট্যাটিক স্ট্রিং আপ।


1

আমি আমার ক্ষেত্রে এই কাজ ঠিক আছে:

public static ManualResetEvent evtToWait = new ManualResetEvent(false);

private static void ReadDataFromConsole( object state )
{
    Console.WriteLine("Enter \"x\" to exit or wait for 5 seconds.");

    while (Console.ReadKey().KeyChar != 'x')
    {
        Console.Out.WriteLine("");
        Console.Out.WriteLine("Enter again!");
    }

    evtToWait.Set();
}

static void Main(string[] args)
{
        Thread status = new Thread(ReadDataFromConsole);
        status.Start();

        evtToWait = new ManualResetEvent(false);

        evtToWait.WaitOne(5000); // wait for evtToWait.Set() or timeOut

        status.Abort(); // exit anyway
        return;
}

1

এটি কি সুন্দর এবং সংক্ষিপ্ত নয়?

if (SpinWait.SpinUntil(() => Console.KeyAvailable, millisecondsTimeout))
{
    ConsoleKeyInfo keyInfo = Console.ReadKey();

    // Handle keyInfo value here...
}

1
স্পিনওয়াইট হ্যাকটি কী?
জন কেটজিক

1

এটি গ্লেন স্লেডেনের সমাধানের পূর্ণ উদাহরণ। আমি অন্য সমস্যার জন্য পরীক্ষার কেস তৈরি করার সময় এটি তৈরি করতে পেরে খুশি হয়েছি। এটি অ্যাসিঙ্ক্রোনাস I / O এবং একটি ম্যানুয়াল পুনরায় সেট ইভেন্ট ব্যবহার করে।

public static void Main() {
    bool readInProgress = false;
    System.IAsyncResult result = null;
    var stop_waiting = new System.Threading.ManualResetEvent(false);
    byte[] buffer = new byte[256];
    var s = System.Console.OpenStandardInput();
    while (true) {
        if (!readInProgress) {
            readInProgress = true;
            result = s.BeginRead(buffer, 0, buffer.Length
              , ar => stop_waiting.Set(), null);

        }
        bool signaled = true;
        if (!result.IsCompleted) {
            stop_waiting.Reset();
            signaled = stop_waiting.WaitOne(5000);
        }
        else {
            signaled = true;
        }
        if (signaled) {
            readInProgress = false;
            int numBytes = s.EndRead(result);
            string text = System.Text.Encoding.UTF8.GetString(buffer
              , 0, numBytes);
            System.Console.Out.Write(string.Format(
              "Thank you for typing: {0}", text));
        }
        else {
            System.Console.Out.WriteLine("oy, type something!");
        }
    }

1

আমার কোডটি সম্পূর্ণরূপে বন্ধুর উত্তরের @JSQuareD- এর ভিত্তিতে তৈরি

তবে আমার Stopwatchটাইমারটি ব্যবহার করার দরকার ছিল কারণ যখন আমি প্রোগ্রামটি শেষ করেছিলাম Console.ReadKey()তখনও Console.ReadLine()এটির জন্য অপেক্ষা করা ছিল এবং এটি অপ্রত্যাশিত আচরণ তৈরি করেছে।

এটা আমার জন্য পুরোপুরি কাজ করেন. আসল কনসোলটি বজায় রাখে e রিডলাইন ()

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("What is the answer? (5 secs.)");
        try
        {
            var answer = ConsoleReadLine.ReadLine(5000);
            Console.WriteLine("Answer is: {0}", answer);
        }
        catch
        {
            Console.WriteLine("No answer");
        }
        Console.ReadKey();
    }
}

class ConsoleReadLine
{
    private static string inputLast;
    private static Thread inputThread = new Thread(inputThreadAction) { IsBackground = true };
    private static AutoResetEvent inputGet = new AutoResetEvent(false);
    private static AutoResetEvent inputGot = new AutoResetEvent(false);

    static ConsoleReadLine()
    {
        inputThread.Start();
    }

    private static void inputThreadAction()
    {
        while (true)
        {
            inputGet.WaitOne();
            inputLast = Console.ReadLine();
            inputGot.Set();
        }
    }

    // omit the parameter to read a line without a timeout
    public static string ReadLine(int timeout = Timeout.Infinite)
    {
        if (timeout == Timeout.Infinite)
        {
            return Console.ReadLine();
        }
        else
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            while (stopwatch.ElapsedMilliseconds < timeout && !Console.KeyAvailable) ;

            if (Console.KeyAvailable)
            {
                inputGet.Set();
                inputGot.WaitOne();
                return inputLast;
            }
            else
            {
                throw new TimeoutException("User did not provide input within the timelimit.");
            }
        }
    }
}


0

উপরে এরিকের পোস্ট বাস্তবায়নের উদাহরণ। এই নির্দিষ্ট উদাহরণটি পাইপের মাধ্যমে কনসোল অ্যাপ্লিকেশনটিতে পাঠানো তথ্য পড়তে ব্যবহৃত হয়েছিল:

 using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

namespace PipedInfo
{
    class Program
    {
        static void Main(string[] args)
        {
            StreamReader buffer = ReadPipedInfo();

            Console.WriteLine(buffer.ReadToEnd());
        }

        #region ReadPipedInfo
        public static StreamReader ReadPipedInfo()
        {
            //call with a default value of 5 milliseconds
            return ReadPipedInfo(5);
        }

        public static StreamReader ReadPipedInfo(int waitTimeInMilliseconds)
        {
            //allocate the class we're going to callback to
            ReadPipedInfoCallback callbackClass = new ReadPipedInfoCallback();

            //to indicate read complete or timeout
            AutoResetEvent readCompleteEvent = new AutoResetEvent(false);

            //open the StdIn so that we can read against it asynchronously
            Stream stdIn = Console.OpenStandardInput();

            //allocate a one-byte buffer, we're going to read off the stream one byte at a time
            byte[] singleByteBuffer = new byte[1];

            //allocate a list of an arbitary size to store the read bytes
            List<byte> byteStorage = new List<byte>(4096);

            IAsyncResult asyncRead = null;
            int readLength = 0; //the bytes we have successfully read

            do
            {
                //perform the read and wait until it finishes, unless it's already finished
                asyncRead = stdIn.BeginRead(singleByteBuffer, 0, singleByteBuffer.Length, new AsyncCallback(callbackClass.ReadCallback), readCompleteEvent);
                if (!asyncRead.CompletedSynchronously)
                    readCompleteEvent.WaitOne(waitTimeInMilliseconds);

                //end the async call, one way or another

                //if our read succeeded we store the byte we read
                if (asyncRead.IsCompleted)
                {
                    readLength = stdIn.EndRead(asyncRead);
                    if (readLength > 0)
                        byteStorage.Add(singleByteBuffer[0]);
                }

            } while (asyncRead.IsCompleted && readLength > 0);
            //we keep reading until we fail or read nothing

            //return results, if we read zero bytes the buffer will return empty
            return new StreamReader(new MemoryStream(byteStorage.ToArray(), 0, byteStorage.Count));
        }

        private class ReadPipedInfoCallback
        {
            public void ReadCallback(IAsyncResult asyncResult)
            {
                //pull the user-defined variable and strobe the event, the read finished successfully
                AutoResetEvent readCompleteEvent = asyncResult.AsyncState as AutoResetEvent;
                readCompleteEvent.Set();
            }
        }
        #endregion ReadPipedInfo
    }
}

0
string readline = "?";
ThreadPool.QueueUserWorkItem(
    delegate
    {
        readline = Console.ReadLine();
    }
);
do
{
    Thread.Sleep(100);
} while (readline == "?");

মনে রাখবেন যে আপনি যদি "কনসোল.আরডকি" রুটে যান তবে আপনি রিডলাইনের কয়েকটি দুর্দান্ত বৈশিষ্ট্য হারাবেন, যথা:

  • মোছা, ব্যাকস্পেস, তীর কী ইত্যাদি জন্য সমর্থন
  • "আপ" কী টিপতে এবং শেষ কমান্ডটির পুনরাবৃত্তি করার ক্ষমতা (যদি আপনি একটি ব্যাকগ্রাউন্ড ডিবাগিং কনসোল প্রয়োগ করেন যা প্রচুর ব্যবহার পায়) তবে এটি খুব কার্যকর হবে।

একটি সময়সীমা যুক্ত করতে, অনুসারে লুপটি পরিবর্তন করুন।


0

বিদ্যমান উত্তরগুলির আধিক্যে আরও একটি সমাধান যোগ করার জন্য দয়া করে আমাকে ঘৃণা করবেন না! এটি কনসোলের জন্য কাজ করে e রিডকি (), তবে সহজেই রিডলাইন () ইত্যাদির সাথে কাজ করার জন্য পরিবর্তন করা যেতে পারে

"কনসোল। পড়ুন" পদ্ধতিগুলি যেমন অবরুদ্ধ হচ্ছে, এটি " ঠেলা " পড়া বাতিল করতে stdin স্ট্রীম।

কলিং সিনট্যাক্স:

ConsoleKeyInfo keyInfo;
bool keyPressed = AsyncConsole.ReadKey(500, out keyInfo);
// where 500 is the timeout

কোড:

public class AsyncConsole // not thread safe
{
    private static readonly Lazy<AsyncConsole> Instance =
        new Lazy<AsyncConsole>();

    private bool _keyPressed;
    private ConsoleKeyInfo _keyInfo;

    private bool DoReadKey(
        int millisecondsTimeout,
        out ConsoleKeyInfo keyInfo)
    {
        _keyPressed = false;
        _keyInfo = new ConsoleKeyInfo();

        Thread readKeyThread = new Thread(ReadKeyThread);
        readKeyThread.IsBackground = false;
        readKeyThread.Start();

        Thread.Sleep(millisecondsTimeout);

        if (readKeyThread.IsAlive)
        {
            try
            {
                IntPtr stdin = GetStdHandle(StdHandle.StdIn);
                CloseHandle(stdin);
                readKeyThread.Join();
            }
            catch { }
        }

        readKeyThread = null;

        keyInfo = _keyInfo;
        return _keyPressed;
    }

    private void ReadKeyThread()
    {
        try
        {
            _keyInfo = Console.ReadKey();
            _keyPressed = true;
        }
        catch (InvalidOperationException) { }
    }

    public static bool ReadKey(
        int millisecondsTimeout,
        out ConsoleKeyInfo keyInfo)
    {
        return Instance.Value.DoReadKey(millisecondsTimeout, out keyInfo);
    }

    private enum StdHandle { StdIn = -10, StdOut = -11, StdErr = -12 };

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetStdHandle(StdHandle std);

    [DllImport("kernel32.dll")]
    private static extern bool CloseHandle(IntPtr hdl);
}

0

এখানে একটি সমাধান যা ব্যবহার করে Console.KeyAvailable। এগুলি ব্লক করা কলগুলি, তবে টিপিএল এর মাধ্যমে এগুলি সংক্ষিপ্তভাবে কল করা মোটামুটি তুচ্ছ হওয়া উচিত desired টাস্ক অ্যাসিঙ্ক্রোনাস প্যাটার্ন এবং সমস্ত ভাল স্টাফ দিয়ে ওয়্যার করা সহজ করার জন্য আমি স্ট্যান্ডার্ড বাতিলকরণ পদ্ধতি ব্যবহার করেছি।

public static class ConsoleEx
{
  public static string ReadLine(TimeSpan timeout)
  {
    var cts = new CancellationTokenSource();
    return ReadLine(timeout, cts.Token);
  }

  public static string ReadLine(TimeSpan timeout, CancellationToken cancellation)
  {
    string line = "";
    DateTime latest = DateTime.UtcNow.Add(timeout);
    do
    {
        cancellation.ThrowIfCancellationRequested();
        if (Console.KeyAvailable)
        {
            ConsoleKeyInfo cki = Console.ReadKey();
            if (cki.Key == ConsoleKey.Enter)
            {
                return line;
            }
            else
            {
                line += cki.KeyChar;
            }
        }
        Thread.Sleep(1);
    }
    while (DateTime.UtcNow < latest);
    return null;
  }
}

এর সাথে কিছু অসুবিধাও রয়েছে।

  • আপনি স্ট্যান্ডার্ড নেভিগেশন বৈশিষ্ট্যগুলি সরবরাহ করেন না যা ReadLineসরবরাহ করে (উপরে / নীচে তীর স্ক্রোলিং ইত্যাদি)।
  • কোনও বিশেষ কী (F1, PrtScn, ইত্যাদি) চাপলে এটি '\ 0' অক্ষরগুলিকে ইনপুটটিতে ইনজেক্ট করে। কোডটি পরিবর্তন করে আপনি এগুলি সহজেই ফিল্টার করে ফেলতে পারেন।

0

এখানেই শেষ হয়েছিল কারণ একটি সদৃশ প্রশ্ন জিজ্ঞাসা করা হয়েছিল। আমি নীচের সমাধানটি নিয়ে এসেছি যা দেখতে সহজ। আমি নিশ্চিত যে এটির কিছু ঘাটতি আমি মিস করেছি।

static void Main(string[] args)
{
    Console.WriteLine("Hit q to continue or wait 10 seconds.");

    Task task = Task.Factory.StartNew(() => loop());

    Console.WriteLine("Started waiting");
    task.Wait(10000);
    Console.WriteLine("Stopped waiting");
}

static void loop()
{
    while (true)
    {
        if ('q' == Console.ReadKey().KeyChar) break;
    }
}

0

আমি এই উত্তরে এসে পৌঁছেছি:

    /// <summary>
    /// Reads Line from console with timeout. 
    /// </summary>
    /// <exception cref="System.TimeoutException">If user does not enter line in the specified time.</exception>
    /// <param name="timeout">Time to wait in milliseconds. Negative value will wait forever.</param>        
    /// <returns></returns>        
    public static string ReadLine(int timeout = -1)
    {
        ConsoleKeyInfo cki = new ConsoleKeyInfo();
        StringBuilder sb = new StringBuilder();

        // if user does not want to spesify a timeout
        if (timeout < 0)
            return Console.ReadLine();

        int counter = 0;

        while (true)
        {
            while (Console.KeyAvailable == false)
            {
                counter++;
                Thread.Sleep(1);
                if (counter > timeout)
                    throw new System.TimeoutException("Line was not entered in timeout specified");
            }

            cki = Console.ReadKey(false);

            if (cki.Key == ConsoleKey.Enter)
            {
                Console.WriteLine();
                return sb.ToString();
            }
            else
                sb.Append(cki.KeyChar);                
        }            
    }

0

একটি সাধারণ উদাহরণ ব্যবহার করে Console.KeyAvailable:

Console.WriteLine("Press any key during the next 2 seconds...");
Thread.Sleep(2000);
if (Console.KeyAvailable)
{
    Console.WriteLine("Key pressed");
}
else
{
    Console.WriteLine("You were too slow");
}

যদি ব্যবহারকারী কীটি টিপায় এবং 2000 মাইলের মধ্যে যেতে দেয়?
ইজজি

0

আরও অনেক সমসাময়িক এবং টাস্ক ভিত্তিক কোড এর মতো দেখতে লাগবে:

public string ReadLine(int timeOutMillisecs)
{
    var inputBuilder = new StringBuilder();

    var task = Task.Factory.StartNew(() =>
    {
        while (true)
        {
            var consoleKey = Console.ReadKey(true);
            if (consoleKey.Key == ConsoleKey.Enter)
            {
                return inputBuilder.ToString();
            }

            inputBuilder.Append(consoleKey.KeyChar);
        }
    });


    var success = task.Wait(timeOutMillisecs);
    if (!success)
    {
        throw new TimeoutException("User did not provide input within the timelimit.");
    }

    return inputBuilder.ToString();
}

0

আমার একটি উইন্ডোজ অ্যাপ্লিকেশন (উইন্ডোজ পরিষেবা) থাকার একটি অনন্য পরিস্থিতি ছিল। ইন্টারেক্টিভভাবে প্রোগ্রামটি চালানোর সময় Environment.IsInteractive(ভিএস ডিবাগার বা সেন্টিমিডি.এক্সই থেকে), আমি আমার স্টিডিন / স্টাডআউট পেতে অ্যাটাচ কনসোল / অলোকসনসোল ব্যবহার করেছি। কাজটি চলাকালীন প্রক্রিয়াটি শেষ হতে না রাখতে ইউআই থ্রেড কল করে Console.ReadKey(false)। আমি অন্য থ্রেড থেকে ইউআই থ্রেডের অপেক্ষারতটি বাতিল করতে চেয়েছিলাম, তাই আমি @ জেএসকিয়ারডডি দ্বারা সমাধানটিতে পরিবর্তন আনলাম।

using System;
using System.Diagnostics;

internal class PressAnyKey
{
  private static Thread inputThread;
  private static AutoResetEvent getInput;
  private static AutoResetEvent gotInput;
  private static CancellationTokenSource cancellationtoken;

  static PressAnyKey()
  {
    // Static Constructor called when WaitOne is called (technically Cancel too, but who cares)
    getInput = new AutoResetEvent(false);
    gotInput = new AutoResetEvent(false);
    inputThread = new Thread(ReaderThread);
    inputThread.IsBackground = true;
    inputThread.Name = "PressAnyKey";
    inputThread.Start();
  }

  private static void ReaderThread()
  {
    while (true)
    {
      // ReaderThread waits until PressAnyKey is called
      getInput.WaitOne();
      // Get here 
      // Inner loop used when a caller uses PressAnyKey
      while (!Console.KeyAvailable && !cancellationtoken.IsCancellationRequested)
      {
        Thread.Sleep(50);
      }
      // Release the thread that called PressAnyKey
      gotInput.Set();
    }
  }

  /// <summary>
  /// Signals the thread that called WaitOne should be allowed to continue
  /// </summary>
  public static void Cancel()
  {
    // Trigger the alternate ending condition to the inner loop in ReaderThread
    if(cancellationtoken== null) throw new InvalidOperationException("Must call WaitOne before Cancelling");
    cancellationtoken.Cancel();
  }

  /// <summary>
  /// Wait until a key is pressed or <see cref="Cancel"/> is called by another thread
  /// </summary>
  public static void WaitOne()
  {
    if(cancellationtoken==null || cancellationtoken.IsCancellationRequested) throw new InvalidOperationException("Must cancel a pending wait");
    cancellationtoken = new CancellationTokenSource();
    // Release the reader thread
    getInput.Set();
    // Calling thread will wait here indefiniately 
    // until a key is pressed, or Cancel is called
    gotInput.WaitOne();
  }    
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.