किसी घटना को C # में निकाल दिए जाने तक कोड का प्रवाह कैसे अवरुद्ध करें


11

यहाँ हम एक के Gridसाथ एक है Button। जब उपयोगकर्ता बटन पर क्लिक करता है, तो उपयोगिता वर्ग में एक विधि निष्पादित की जाती है जो एप्लिकेशन को ग्रिड पर एक क्लिक प्राप्त करने के लिए मजबूर करती है। कोड प्रवाह यहां रुकना चाहिए और तब तक जारी नहीं होना चाहिए जब तक उपयोगकर्ता ने क्लिक नहीं किया है Grid

मेरा यहाँ पहले भी इसी तरह का सवाल रहा है:

उपयोगकर्ता C # WPF पर क्लिक करने तक प्रतीक्षा करें

उस प्रश्न में, मुझे एसिंक्स / वेट का उपयोग करके एक उत्तर मिला, जो काम करता है, लेकिन जब से मैं इसे एपीआई के हिस्से के रूप में उपयोग करने जा रहा हूं, मैं एसिंक्स / वेट का उपयोग नहीं करना चाहता हूं, क्योंकि उपभोक्ताओं के पास अपने तरीकों को चिह्नित करने के लिए होगा async जो मुझे नहीं चाहिए।

Utility.PickPoint(Grid grid)इस लक्ष्य को प्राप्त करने के लिए मैं विधि कैसे लिखूं ?

मैंने इसे देखा जो मदद कर सकता है लेकिन पूरी तरह से समझ नहीं पाया कि ईमानदार होने के लिए यहां आवेदन करें:

किसी घटना के पूरा होने तक अवरुद्ध होना

इसे Console.ReadKey () मेथड कन्सोल एप्लीकेशन की तरह समझें। जब हम इस विधि को कहते हैं, तो कोड प्रवाह बंद हो जाता है जब तक हम कुछ मूल्य दर्ज नहीं करते हैं। डिबगर तब तक जारी नहीं होता जब तक हम कुछ दर्ज नहीं करते। मैं PickPoint () विधि के लिए सटीक व्यवहार चाहता हूं। जब तक उपयोगकर्ता ग्रिड पर क्लिक नहीं करेगा तब तक कोड प्रवाह बंद हो जाएगा।

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>

        <Grid x:Name="View" Background="Green"/>
        <Button Grid.Row="1" Content="Pick" Click="ButtonBase_OnClick"/>
    </Grid>
</Window>

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        // do not continue the code flow until the user has clicked on the grid. 
        // so when we debug, the code flow will literally stop here.
        var point = Utility.PickPoint(View);


        MessageBox.Show(point.ToString());
    }
}

public static class Utility
{
    public static Point PickPoint(Grid grid)
    {

    }
}

स्पष्ट तरीका यह है Aync/Awaitकि ऑपरेशन A को करने और उस ऑपरेशन STATE को सहेजने के बारे में अब आप चाहते हैं कि उपयोगकर्ता ग्रिड पर क्लिक करें। यदि उपयोगकर्ता क्लिक करता है तो आप राज्य की जांच करते हैं यदि सत्य है तो क्या आपका ऑपरेशन बाकी है जो आप करना चाहते हैं?
राव हमास हुसैन

@ राओ हम्मासहुसैन ने मेरे प्रश्न को एक लिंक के साथ अद्यतन किया जो मदद कर सकता है। उपयोगिता विधि एक एपीआई का हिस्सा होगी जिसे एपीआई का उपयोगकर्ता कॉल करेगा जब भी वह स्क्रीन पर क्लिक करने के लिए अंतिम उपयोगकर्ता से अनुरोध करना चाहेगा। इसे सामान्य विंडो एप्लिकेशन या Console.Readline () विधि में टेक्स्ट के लिए एक प्रॉम्प्ट विंडो की तरह समझें। इन मामलों में, कोड प्रवाह बंद हो जाता है जब तक कि उपयोगकर्ता कुछ दर्ज नहीं करता है। अब मुझे सटीक बात चाहिए लेकिन इस समय उपयोगकर्ता स्क्रीन पर क्लिक करता है।
वाहिद

AutoResetEventवह नहीं है जो आप चाहते हैं?
राव हमास हुसैन

@ राओ हम्मासहुसैन मुझे ऐसा लगता है लेकिन वास्तव में यह नहीं पता है कि इसका उपयोग कैसे करना है।
वाहिद

यह ऐसा है जैसे आप जानबूझकर WAIT STATE को लागू कर रहे हैं। क्या यह वास्तव में आवश्यक है? cuz क्या आप इसे var point = Utility.PickPoint(Grid grid);ग्रिड क्लिक विधि में नहीं डाल सकते हैं ? कुछ ऑपरेशन करते हैं और प्रतिक्रिया वापस करते हैं?
राव हमास हुसैन

जवाबों:


9

"किसी घटना के निकाल दिए जाने तक कोड प्रवाह कैसे अवरुद्ध करें?"

आपका दृष्टिकोण गलत है। इवेंट-चालित का मतलब किसी इवेंट के लिए ब्लॉक करना और इंतजार करना नहीं है। आप कभी भी प्रतीक्षा न करें, कम से कम आप हमेशा इससे बचने की भरसक कोशिश करते हैं। वेटिंग संसाधनों को बर्बाद कर रहा है, थ्रेड्स को अवरुद्ध कर रहा है और शायद एक गतिरोध या ज़ोंबी थ्रेड के जोखिम का परिचय दे रहा है (यदि रिलीज का संकेत कभी नहीं उठाया जाता है)।
यह स्पष्ट होना चाहिए कि किसी घटना की प्रतीक्षा करने के लिए एक धागा को अवरुद्ध करना एक विरोधी पैटर्न है क्योंकि यह एक घटना के विचार का खंडन करता है।

आपके पास आम तौर पर दो (आधुनिक) विकल्प होते हैं: एक अतुल्यकालिक एपीआई या एक घटना-संचालित एपीआई को लागू करें। चूंकि आप अपने एपीआई को एसिंक्रोनस लागू नहीं करना चाहते हैं, इसलिए आपको इवेंट-संचालित एपीआई के साथ छोड़ दिया जाता है।

ईवेंट-चालित API की कुंजी यह है कि कॉल करने वाले को परिणाम के लिए समकालिक रूप से प्रतीक्षा करने या परिणाम के लिए सर्वेक्षण करने के लिए मजबूर करने के बजाय, आप कॉलर को जारी रखने दें और उसे एक अधिसूचना भेजें, जब परिणाम तैयार हो जाता है या ऑपरेशन पूरा हो जाता है। इस बीच, कॉल करने वाला अन्य कार्यों को निष्पादित करना जारी रख सकता है।

जब थ्रेडिंग के नजरिए से समस्या को देखते हैं, तो ईवेंट-चालित एपीआई कॉलिंग थ्रेड को अनुमति देता है जैसे, UI थ्रेड, जो बटन के ईवेंट हैंडलर को निष्पादित करता है, यूआई से संबंधित अन्य संचालन जैसे कि यूआई तत्वों को रेंडर करने के लिए जारी रखने के लिए स्वतंत्र होना चाहिए। या माउस आंदोलन और प्रमुख प्रेस की तरह उपयोगकर्ता इनपुट से निपटने। ईवेंट-चालित एपीआई का एक अतुल्यकालिक एपीआई की तरह ही प्रभाव या लक्ष्य होता है, हालांकि यह बहुत कम सुविधाजनक है।

चूंकि आपने वास्तव में क्या करने की कोशिश कर रहे हैं, इस पर पर्याप्त विवरण नहीं दिया है कि वास्तव में क्या कर रहे Utility.PickPoint()हैं और कार्य का परिणाम क्या है या उपयोगकर्ता को `ग्रिड पर क्लिक करना है, तो मैं आपको एक बेहतर समाधान प्रदान नहीं कर सकता। । मैं सिर्फ अपनी आवश्यकता को लागू करने के सामान्य तरीके की पेशकश कर सकता हूं।

आपके प्रवाह या लक्ष्य को स्पष्ट रूप से कम से कम दो चरणों में विभाजित किया गया है ताकि इसे संचालन का क्रम बनाया जा सके:

  1. उपयोगकर्ता द्वारा बटन क्लिक करने पर ऑपरेशन 1 निष्पादित करें
  2. जब उपयोगकर्ता क्लिक करता है, तब ऑपरेशन 2 (जारी / पूरा ऑपरेशन 1) जारी रखें Grid

कम से कम दो बाधाओं के साथ:

  1. वैकल्पिक: एपीआई क्लाइंट को इसे दोहराने की अनुमति देने से पहले अनुक्रम पूरा होना चाहिए। एक अनुक्रम पूरा हो गया है जब ऑपरेशन 2 पूरा होने के लिए चला गया है।
  2. ऑपरेशन 1 हमेशा ऑपरेशन से पहले निष्पादित किया जाता है। ऑपरेशन 1 अनुक्रम शुरू करता है।
  3. ऑपरेशन 2 को निष्पादित करने के लिए एपीआई क्लाइंट को अनुमति देने से पहले ऑपरेशन 1 को पूरा करना होगा

गैर-अवरोधक इंटरैक्शन की अनुमति देने के लिए एपीआई के क्लाइंट के लिए दो सूचनाओं (घटनाओं) की आवश्यकता होती है:

  1. ऑपरेशन 1 पूरा (या आवश्यक बातचीत)
  2. ऑपरेशन 2 (या लक्ष्य) पूरा हुआ

आपको दो सार्वजनिक तरीकों और दो सार्वजनिक घटनाओं को उजागर करके अपने एपीआई को इस व्यवहार और बाधाओं को लागू करने देना चाहिए।

चूंकि यह कार्यान्वयन केवल एपीआई (एकल-समवर्ती) कॉल की अनुमति देता है एपीआई के लिए यह IsBusyएक रनिंग अनुक्रम को इंगित करने के लिए एक संपत्ति को उजागर करने की भी सिफारिश की जाती है । यह एक नया अनुक्रम शुरू करने से पहले वर्तमान स्थिति को मतदान करने की अनुमति देता है, हालांकि बाद की कॉल को निष्पादित करने के लिए पूर्ण घटना की प्रतीक्षा करने की सिफारिश की जाती है।

यूटिलिटी एपीआई को लागू / रिफलेक्टर

Utility.cs

class Utility
{
  public event EventHandler InitializePickPointCompleted;
  public event EventHandler<PickPointCompletedEventArgs> PickPointCompleted;
  public bool IsBusy { get; set; }
  private bool IsPickPointInitialized { get; set; }

  // The prefix 'Begin' signals the caller or client of the API, 
  // that he also has to end the sequence explicitly
  public void BeginPickPoint(param)
  {
    // Implement constraint 1
    if (this.IsBusy)
    {
      // Alternatively just return or use Try-do pattern
      throw new InvalidOperationException("BeginPickPoint is already executing. Call EndPickPoint before starting another sequence.");
    }

    // Set the flag that a current sequence is in progress
    this.IsBusy = true;

    // Execute operation until caller interaction is required.
    // Execute in background thread to allow API caller to proceed with execution.
    Task.Run(() => StartOperationNonBlocking(param));
  }

  public void EndPickPoint(param)
  {
    // Implement constraint 2 and 3
    if (!this.IsPickPointInitialized)
    {
      // Alternatively just return or use Try-do pattern
      throw new InvalidOperationException("BeginPickPoint must have completed execution before calling EndPickPoint.");
    }

    // Execute operation until caller interaction is required.
    // Execute in background thread to allow API caller to proceed with execution.
    Task.Run(() => CompleteOperationNonBlocking(param));
  }

  private void StartOperationNonBlocking(param)
  {
    ... // Do something

    // Flag the completion of the first step of the sequence (to guarantee constraint 2)
    this.IsPickPointInitialized = true;

    // Request caller interaction to kick off EndPickPoint() execution
    OnInitializePickPointCompleted();
  }

  private void CompleteOperationNonBlocking(param)
  {
    // Execute goal and get the result of the completed task
    Point result = ExecuteGoal();

    // Reset API sequence (allow next client invocation)
    this.IsBusy = false;
    this.IsPickPointInitialized = false;

    // Notify caller that execution has completed and the result is available
    OnPickPointCompleted(result);
  }

  private void OnInitializePickPointCompleted()
  {
    // Set the result of the task
    this.InitializePickPointCompleted?.Invoke(this, EventArgs.Empty);
  }

  private void OnPickPointCompleted(Point result)
  {
    // Set the result of the task
    this.PickPointCompleted?.Invoke(this, new PickPointCompletedEventArgs(result));
  }
}

PickPointCompletedEventArgs.cs

class PickPointCompletedEventArgs : AsyncCompletedEventArgs 
{
  public Point Result { get; }

  public PickPointCompletedEventArgs(Point result)
  {
    this.Result = result;
  }
}

एपीआई का उपयोग करें

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private Utility Api { get; set; }

  public MainWindow()
  {
    InitializeComponent();

    this.Api = new Utility();
  }

  private void StartPickPoint_OnButtonClick(object sender, RoutedEventArgs e)
  {
    this.Api.InitializePickPointCompleted += RequestUserInput_OnInitializePickPointCompleted;

    // Invoke API and continue to do something until the first step has completed.
    // This is possible because the API will execute the operation on a background thread.
    this.Api.BeginPickPoint();
  }

  private void RequestUserInput_OnInitializePickPointCompleted(object sender, EventArgs e)
  {
    // Cleanup
    this.Api.InitializePickPointCompleted -= RequestUserInput_OnInitializePickPointCompleted;

    // Communicate to the UI user that you are waiting for him to click on the screen
    // e.g. by showing a Popup, dimming the screen or showing a dialog.
    // Once the input is received the input event handler will invoke the API to complete the goal   
    MessageBox.Show("Please click the screen");  
  }

  private void FinishPickPoint_OnGridMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  {
    this.Api.PickPointCompleted += ShowPoint_OnPickPointCompleted;

    // Invoke API to complete the goal
    // and continue to do something until the last step has completed
    this.Api.EndPickPoint();
  }

  private void ShowPoint_OnPickPointCompleted(object sender, PickPointCompletedEventArgs e)
  {
    // Cleanup
    this.Api.PickPointCompleted -= ShowPoint_OnPickPointCompleted;

    // Get the result from the PickPointCompletedEventArgs instance
    Point point = e.Result;

    // Handle the result
    MessageBox.Show(point.ToString());
  }
}

MainWindow.xaml

<Window>
  <Grid MouseLeftButtonUp="FinishPickPoint_OnGridMouseLeftButtonUp">
    <Button Click="StartPickPoint_OnButtonClick" />
  </Grid>
</Window>

टिप्पणियों

पृष्ठभूमि थ्रेड पर उठाए गए इवेंट उसी थ्रेड पर अपने हैंडलर निष्पादित करेंगे। DispatcherObjectएक हैंडलर से एक यूआई तत्व की तरह एक्सेस करना , जिसे एक बैकग्राउंड थ्रेड पर निष्पादित किया जाता है, क्रिटिकल ऑपरेशन को या Dispatcherतो उपयोग करने के लिए Dispatcher.Invokeया Dispatcher.InvokeAsyncक्रॉस-थ्रेड अपवादों से बचने के लिए आवश्यक है। डिस्पैचर आत्मीयता या थ्रेड एफिनिटी नामक इस घटना के बारे में अधिक जानने के
लिए टिप्पणियों को पढ़ें DispatcherObject
एपीआई के सुविधाजनक उपयोग के लिए मैं सभी घटनाओं को कॉलर के मूल संदर्भ में या तो कैप्चर करने और कॉल करने वाले का SynchronizationContextउपयोग करके AsyncOperationया (या AsyncOperationManager) का उपयोग करने का सुझाव देता हूं ।

उपर्युक्त उदाहरण को आसानी से रद्द करके (उदाहरण के लिए) एक Cancel()विधि जैसे, PickPointCancel()और प्रगति रिपोर्टिंग (अधिमानतः उपयोग करके Progress<T>) प्रदान करके बढ़ाया जा सकता है ।


कुछ विचार - आपकी टिप्पणियों का उत्तर देते हैं

क्योंकि आप "बेहतर" अवरोधक समाधान खोजने के लिए मुझसे संपर्क कर रहे थे, इसलिए मुझे सांत्वना अनुप्रयोगों का उदाहरण दिया, मैंने आपको समझा दिया, कि आपकी धारणा या दृष्टिकोण पूरी तरह से गलत है।

"कोड की इन दो पंक्तियों के साथ एक कंसोल एप्लिकेशन पर विचार करें।

var str = Console.ReadLine(); 
Console.WriteLine(str);

जब आप डिबग मोड में एप्लिकेशन निष्पादित करते हैं तो क्या होता है। यह कोड की पहली पंक्ति पर रुक जाएगा और आपको कंसोल यूआई में एक मान दर्ज करने के लिए मजबूर करेगा और उसके बाद आप कुछ दर्ज करें और Enter दबाएं, यह अगली पंक्ति को निष्पादित करेगा और वास्तव में आपके द्वारा दर्ज किए गए प्रिंट करें। मैं बिल्कुल उसी व्यवहार के बारे में सोच रहा था लेकिन डब्ल्यूपीएफ एप्लिकेशन में। ”

एक कंसोल एप्लिकेशन कुछ पूरी तरह से अलग है। थ्रेडिंग कॉन्सेप्ट थोड़ा अलग है। कंसोल अनुप्रयोगों में GUI नहीं है। बस इनपुट / आउटपुट / एरर स्ट्रीम। आप एक कंसोल एप्लीकेशन के आर्किटेक्चर की तुलना जीयूआई एप्लीकेशन से नहीं कर सकते। यह काम नहीं करेगा। आपको वास्तव में इसे समझना और स्वीकार करना चाहिए।

इसके अलावा लगता है धोखा नहीं है । क्या आप जानते हैं कि अंदर क्या हो रहा हैConsole.ReadLine ? इसे कैसे लागू किया जाता है ? क्या यह मुख्य धागे को अवरुद्ध कर रहा है और समानांतर में यह इनपुट पढ़ता है? या यह सिर्फ मतदान है?
यहाँ इसका मूल कार्यान्वयन है Console.ReadLine:

public virtual String ReadLine() 
{
  StringBuilder sb = new StringBuilder();
  while (true) 
  {
    int ch = Read();
    if (ch == -1) 
      break;
    if (ch == '\r' || ch == '\n') 
    {
      if (ch == '\r' && Peek() == '\n') 
        Read();
      return sb.ToString();
    }
    sb.Append((char)ch);
  }
  if (sb.Length > 0) 
    return sb.ToString();
  return null;
}

जैसा कि आप देख सकते हैं कि यह एक सरल तुल्यकालिक ऑपरेशन है। यह एक "अनंत" लूप में उपयोगकर्ता इनपुट के लिए मतदान करता है। कोई जादू ब्लॉक और जारी नहीं।

WPF एक रेंडरिंग थ्रेड और UI थ्रेड के आसपास बना है। उन थ्रेड हमेशा ओएस के साथ संवाद करने के लिए कताई करते रहते हैं जैसे उपयोगकर्ता इनपुट को संभालना - एप्लिकेशन को उत्तरदायी रखना । आप कभी भी इस थ्रेड को रोकना / ब्लॉक नहीं करना चाहते क्योंकि यह आवश्यक पृष्ठभूमि के काम को करने से फ्रेमवर्क को रोक देगा, जैसे कि माउस ईवेंट का जवाब देना - आप नहीं चाहते कि माउस फ्रीज़ हो:

प्रतीक्षा करना = थ्रेड ब्लॉक करना = अप्रतिष्ठित होना = बुरा यूएक्स = परेशान उपयोगकर्ता / ग्राहक = कार्यालय में परेशानी।

कभी-कभी, एप्लिकेशन प्रवाह को इनपुट या एक रूटीन को पूरा करने के लिए इंतजार करना पड़ता है। लेकिन हम मुख्य सूत्र को अवरुद्ध नहीं करना चाहते हैं।
इसलिए लोगों ने जटिल अतुल्यकालिक प्रोग्रामिंग मॉडल का आविष्कार किया, ताकि मुख्य धागे को अवरुद्ध किए बिना प्रतीक्षा करने और डेवलपर को जटिल और गलत तरीके से मल्टीथ्रेडिंग कोड लिखने के लिए मजबूर किया जा सके।

प्रत्येक आधुनिक एप्लिकेशन फ्रेमवर्क सरल और कुशल कोड के विकास की अनुमति देने के लिए अतुल्यकालिक संचालन या एक अतुल्यकालिक प्रोग्रामिंग मॉडल प्रदान करता है।

यह तथ्य कि आप अतुल्यकालिक प्रोग्रामिंग मॉडल का विरोध करने के लिए कड़ी मेहनत कर रहे हैं, मेरे लिए कुछ समझ की कमी दर्शाता है। हर आधुनिक डेवलपर एक तुल्यकालिक एक पर एक अतुल्यकालिक एपीआई पसंद करता है। कोई भी गंभीर डेवलपर awaitकीवर्ड का उपयोग करने या उसकी विधि घोषित करने की परवाह नहीं करता है async। कोई भी नहीं। आप पहले I मुठभेड़ हैं जो अतुल्यकालिक एपीआई के बारे में शिकायत करते हैं और जो उन्हें उपयोग करने के लिए असुविधाजनक पाते हैं।

अगर मैं आपके फ्रेमवर्क की जांच करूंगा, जो UI संबंधित समस्याओं को हल करने या UI से संबंधित कार्यों को आसान बनाने का लक्ष्य रखता है, तो मैं उम्मीद करूंगा कि यह अतुल्यकालिक हो - सभी तरह से।
यूआई संबंधित एपीआई जो अतुल्यकालिक नहीं है, बेकार है, क्योंकि यह मेरी प्रोग्रामिंग शैली को जटिल करेगा, इसलिए मेरा कोड जो इसलिए अधिक त्रुटि-प्रवण और बनाए रखना मुश्किल हो जाता है।

एक अलग परिप्रेक्ष्य: जब आप स्वीकार करते हैं कि प्रतीक्षा UI थ्रेड को ब्लॉक करता है, तो बहुत बुरा और अवांछनीय उपयोगकर्ता अनुभव बना रहा है क्योंकि UI समाप्त होने तक फ्रीज हो जाएगा, अब जब आपको पता चलता है, तो आप एपीआई या प्लगइन मॉडल की पेशकश क्यों करेंगे एक डेवलपर को यह करने के लिए प्रोत्साहित करता है - प्रतीक्षा को लागू करें?
आप नहीं जानते कि तीसरा पक्ष प्लगइन क्या करेगा और एक रूटीन पूरा होने तक कितना समय लगेगा। यह बस एक बुरा एपीआई डिजाइन है। जब आपका एपीआई UI थ्रेड पर काम करता है तो आपके एपीआई के कॉलर को गैर-अवरोधक कॉल करने में सक्षम होना चाहिए।

यदि आप एकमात्र सस्ते या सुशोभित समाधान से इनकार करते हैं, तो मेरे उदाहरण में दिखाए गए घटना-संचालित दृष्टिकोण का उपयोग करें।
यह वही करता है जो आप चाहते हैं: एक दिनचर्या शुरू करें - उपयोगकर्ता इनपुट की प्रतीक्षा करें - निष्पादन जारी रखें - लक्ष्य पूरा करें।

मैंने वास्तव में यह समझाने की कई बार कोशिश की कि प्रतीक्षा / अवरोधन एक खराब एप्लिकेशन डिज़ाइन क्यों है। फिर, आप एक कंसोल UI की तुलना एक समृद्ध ग्राफिकल यूआई से नहीं कर सकते हैं, जहां अकेले इनपुट स्ट्रीम को सुनने की तुलना में अकेले इनपुट हैंडलिंग एक भीड़ अधिक जटिल है। मुझे वास्तव में आपके अनुभव स्तर का पता नहीं है और आपने कहाँ शुरू किया है, लेकिन आपको एसिंक्रोनस प्रोग्रामिंग मॉडल को गले लगाना शुरू करना चाहिए। मैं इसका कारण नहीं जानता कि आप इससे बचने की कोशिश क्यों करते हैं। लेकिन यह बिल्कुल भी समझदारी नहीं है।

आज अतुल्यकालिक प्रोग्रामिंग मॉडल हर जगह, हर प्लेटफॉर्म, कंपाइलर, हर पर्यावरण, ब्राउज़र, सर्वर, डेस्कटॉप, डेटाबेस पर - हर जगह लागू होते हैं। ईवेंट-चालित मॉडल एक ही लक्ष्य को प्राप्त करने की अनुमति देता है, लेकिन पृष्ठभूमि के थ्रेड्स पर भरोसा करते हुए (घटनाओं के बारे में जानने के लिए डॉक्स (जब डॉक्स हैं) पढ़ें) का उपयोग करना (सब्सक्राइब करना / अनसब्सक्राइब करना) कम सुविधाजनक है। ईवेंट-चालित पुराने जमाने का है और इसका उपयोग केवल तब किया जाना चाहिए जब अतुल्यकालिक लाइब्रेरी उपलब्ध न हों या लागू न हों।

एक साइड-नोट के रूप में: .NET फ्रेमवर्क (.NET मानक) TaskCompletionSourceएक मौजूदा समतुल्य एपीआई को एक अतुल्यकालिक एपीआई में बदलने के लिए एक सरल तरीका प्रदान करने के लिए (अन्य उद्देश्यों के बीच ) प्रदान करता है।

"मैंने ऑटोडेस्क रेविट में सटीक व्यवहार देखा है।"

व्यवहार (जो आप अनुभव करते हैं या निरीक्षण करते हैं) इस अनुभव को लागू करने के तरीके से बहुत अलग है। दो अलग बातें। आपका ऑटोडेस्क अतुल्यकालिक पुस्तकालयों या भाषा सुविधाओं या कुछ अन्य थ्रेडिंग तंत्र का उपयोग करने की संभावना है। और यह संदर्भ से संबंधित भी है। जब आपके दिमाग में जो विधि होती है, वह एक बैकग्राउंड थ्रेड पर निष्पादित होती है, तब डेवलपर इस थ्रेड को ब्लॉक करने का विकल्प चुन सकता है। उसके पास ऐसा करने के लिए या तो एक बहुत अच्छा कारण है या सिर्फ एक खराब डिजाइन विकल्प है। आप पूरी तरह से गलत रास्ते पर हैं;) अवरुद्ध करना अच्छा नहीं है।
(क्या ऑटोडेस्क सोर्स कोड ओपन सोर्स है? या आप कैसे जानते हैं कि इसे कैसे लागू किया जाता है?)

मैं आपको नाराज नहीं करना चाहता, कृपया मेरी बात पर विश्वास करें। लेकिन कृपया अपने एपीआई अतुल्यकालिक को लागू करने के लिए पुनर्विचार करें। यह केवल आपके सिर में है कि डेवलपर्स को एसिंक्स / वेट का उपयोग करना पसंद नहीं है। आपको स्पष्ट रूप से गलत मानसिकता मिली। और उस कंसोल एप्लिकेशन तर्क के बारे में भूल जाओ - यह बकवास है;)

यूआई संबंधित एपीआई चाहिए का उपयोग async / इंतजार जब भी संभव हो। अन्यथा, आप अपने एपीआई के ग्राहक को गैर-अवरोधक कोड लिखने के लिए सभी काम छोड़ देते हैं। आप मुझे हर कॉल को अपने बैकग्राउंड थ्रेड में एपीआई में लपेटने के लिए मजबूर करेंगे। या कम आरामदायक ईवेंट हैंडलिंग का उपयोग करने के लिए। मेरा विश्वास करो - हर डेवलपर asyncइवेंट हैंडलिंग करने की तुलना में अपने सदस्यों को सजाता है । हर बार जब आप घटनाओं का उपयोग करते हैं तो आप एक संभावित मेमोरी रिसाव का जोखिम उठा सकते हैं - कुछ परिस्थितियों पर निर्भर करता है, लेकिन प्रोग्रामिंग लापरवाह होने पर जोखिम वास्तविक और दुर्लभ नहीं है।

मैं वास्तव में आशा करता हूं कि आप समझ गए होंगे कि ब्लॉकिंग खराब क्यों है। मैं वास्तव में आशा करता हूं कि आप एक आधुनिक अतुल्यकालिक एपीआई लिखने के लिए async / प्रतीक्षा का उपयोग करने का निर्णय लेते हैं। फिर भी, मैंने आपको घटनाओं का उपयोग करके गैर-अवरोधक प्रतीक्षा करने के लिए एक बहुत ही सामान्य तरीका दिखाया, हालांकि मैं आपसे async / प्रतीक्षा का उपयोग करने का आग्रह करता हूं।

"एपीआई प्रोग्रामर को UI और आदि तक पहुंचने की अनुमति देगा। मान लीजिए कि प्रोग्रामर एक ऐड-इन विकसित करना चाहता है, जब एक बटन पर क्लिक किया जाता है, तो अंतिम उपयोगकर्ता को यूआई में एक बिंदु चुनने के लिए कहा जाता है"

यदि आप प्लगइन को यूआई तत्वों तक सीधी पहुंच की अनुमति नहीं देना चाहते हैं, तो आपको घटनाओं को सौंपने या सार वाले ऑब्जेक्ट के माध्यम से आंतरिक घटकों को उजागर करने के लिए एक इंटरफ़ेस प्रदान करना चाहिए।
API आंतरिक रूप से ऐड-इन की ओर से UI ईवेंट की सदस्यता लेगा और फिर API क्लाइंट को संबंधित "आवरण" ईवेंट को उजागर करके ईवेंट को दर्शाता है। आपके API को कुछ हुक की पेशकश करनी चाहिए जहां ऐड-इन विशिष्ट एप्लिकेशन घटकों तक पहुंचने के लिए कनेक्ट हो सकता है। एक प्लगइन एपीआई एक एडेप्टर या मुखौटे की तरह कार्य करता है ताकि बाहरी लोगों को आंतरिक तक पहुंच मिल सके।
अलगाव की डिग्री की अनुमति देने के लिए।

विज़ुअल स्टूडियो कैसे प्लग इन का प्रबंधन करता है या हमें उन्हें कार्यान्वित करने की अनुमति देता है इस पर एक नज़र डालें। Pretend आप विजुअल स्टूडियो के लिए एक प्लगइन लिखना चाहते हैं और यह कैसे करना है पर कुछ शोध करते हैं। आपको एहसास होगा कि विज़ुअल स्टूडियो एक इंटरफेस या एपीआई के माध्यम से अपने इंटर्नल को उजागर करता है। ईजी आप कोड संपादक में हेरफेर कर सकते हैं या इसके वास्तविक उपयोग के बिना संपादक की सामग्री के बारे में जानकारी प्राप्त कर सकते हैं ।


नमस्ते, सवाल को दूसरे दृष्टिकोण से जानने के लिए धन्यवाद। क्षमा करें यदि प्रश्न विवरण पर थोड़ा अस्पष्ट था। इसमें कोड की इन दो पंक्तियों के साथ एक कंसोल एप्लिकेशन पर विचार करें। var str = Console.ReadLine(); Console.WriteLine(str);जब आप डिबग मोड में एप्लिकेशन निष्पादित करते हैं तो क्या होता है। यह कोड की पहली पंक्ति पर रुक जाएगा और आपको कंसोल यूआई में एक मूल्य दर्ज करने के लिए मजबूर करेगा और उसके बाद आप कुछ दर्ज करें और Enter दबाएं, यह अगली पंक्ति को निष्पादित करेगा और वास्तव में आपके द्वारा दर्ज किए गए प्रिंट करें। मैं बिल्कुल उसी व्यवहार के बारे में सोच रहा था लेकिन डब्ल्यूपीएफ एप्लिकेशन में।
वाहिद

सीएडी एप्लिकेशन में मैं विकसित कर रहा हूं, उपयोगकर्ताओं को इसे एडिंस / प्लगइन्स द्वारा विस्तारित करने में सक्षम होना चाहिए। एपीआई प्रोग्रामर को यूआई और आदि तक पहुंच प्रदान करने की अनुमति देगा। अब मान लीजिए प्रोग्रामर एक ऐडिन विकसित करना चाहता है कि जब एक बटन पर क्लिक किया जाता है, तो अंतिम उपयोगकर्ता को यूआई में एक बिंदु चुनने के लिए कहा जाता है और फिर कोड अन्य कार्य करेगा दिए गए बिंदु के साथ ठंडा सामान। हो सकता है कि वे एक और बिंदु लेने के लिए कहेंगे और एक रेखा
खींचेंगे

मैंने Autodesk Revit में सटीक व्यवहार देखा है।
वाहिद

1
मुझे आपकी आवश्यकता के बारे में कुछ कहना है। कृपया मेरा अद्यतन उत्तर पढ़ें। मैंने वहां उत्तर पोस्ट किया क्योंकि यह किसी भी तरह लंबा हो गया। मैं मानता हूं कि आपने वास्तव में मुझे ट्रिगर किया है। कृपया, पढ़ते समय ध्यान रखें कि मैं आपको अपमानित नहीं करना चाहता।
बायोनिककोड

आपके अद्यतन उत्तर के लिए धन्यवाद। बेशक, मैं नाराज नहीं हूं। इसके विपरीत, आपने इस पर जो समय और प्रयास रखा है, उसके लिए मैं वास्तव में आभारी हूं।
वाहिद

5

मुझे व्यक्तिगत रूप से लगता है कि यह सभी के द्वारा अति-जटिल हो रहा है, लेकिन शायद मैं इस कारण को पूरी तरह से नहीं समझ पा रहा हूँ कि इसे एक निश्चित तरीके से करने की आवश्यकता क्यों है, लेकिन ऐसा लगता है कि यहाँ एक साधारण बूल चेक का उपयोग किया जा सकता है।

और सबसे पहले, अपने ग्रिड को गुणों Backgroundऔर IsHitTestVisibleगुणों को सेट करके हिट-टेस्टेबल बनाएं , अन्यथा यह माउस क्लिक पर भी कब्जा नहीं करेगा।

<grid MouseLeftButtonUp="Grid_MouseLeftButtonUp" IsHitTestVisible="True" Background="Transparent">

अगला एक बूल मान बनाएँ जो स्टोर कर सकता है कि क्या "ग्रिडक्लिक" घटना होनी चाहिए। जब ग्रिड पर क्लिक किया जाता है, तो उस मूल्य की जाँच करें और यदि क्लिक का इंतजार है तो ग्रिड क्लिक इवेंट से निष्पादन निष्पादित करें।

उदाहरण:

bool awaitingClick = false;


private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
   awaitingClick=true;
}

private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{     
     //Stop here if the program shouldn't do anything when grid is clicked
     if (!awaitingClick) { return; } 

     //Run event
     var point = Utility.PickPoint(View);
     MessageBox.Show(point.ToString());

     awaitingClick=false;//Reset
}

हाय ट्रॉनल्ड, मुझे लगता है कि आपने प्रश्न को गलत समझा है। उपयोगिता के लिए कोड को रोकने के लिए मुझे क्या चाहिए। विकिपीडिया (देखें) और उपयोगकर्ता द्वारा ग्रिड पर क्लिक करने के बाद ही जारी रखें।
वाहिद

अरे हाँ, मैं पूरी तरह से गलत समझा। मेरी क्षमायाचना, मुझे महसूस नहीं हुआ कि आपको वास्तव में रोकने के लिए सब कुछ चाहिए। मुझे नहीं लगता कि मल्टी-थ्रेडिंग के बिना यह संभव है क्योंकि पूरे यूआई को ब्लॉक किया जाएगा।
ट्रॉनल्ड

मुझे अभी भी यकीन नहीं है कि यह संभव नहीं है। यह निश्चित रूप से async / प्रतीक्षा के साथ संभव है जो एक बहु-थ्रेडेड समाधान नहीं है। लेकिन जो मुझे चाहिए वह async / प्रतीक्षा समाधान का एक विकल्प है।
वाहिद

1
ज़रूर, लेकिन आपने उल्लेख किया है कि आप async / प्रतीक्षा का उपयोग नहीं कर सकते। ऐसा लगता है कि आपको डिस्पैचर और एक थ्रेड का उपयोग करने की आवश्यकता होगी जो मुख्य थ्रेड (जो UI पर निष्पादित होता है) से अलग हो गया है। मुझे आशा है कि आप एक और तरीका खोज लेंगे, क्योंकि मैं खुद रुचि रखता हूं
ट्रॉनल्ड

2

मैंने कुछ चीजों की कोशिश की, लेकिन मैं इसके बिना नहीं कर पा रहा हूं async/await। क्योंकि यदि हम इसका उपयोग नहीं करते हैं तो यह कारण बनता है DeadLockया UI अवरुद्ध है और फिर हम Grid_Clickइनपुट लेने में सक्षम हैं ।

private async void ToolBtn_OnClick(object sender, RoutedEventArgs e)
{
    var senderBtn = sender as Button;
    senderBtn.IsEnabled = false;

    var response = await Utility.PickPoint(myGrid);
    MessageBox.Show(response.ToString());
    senderBtn.IsEnabled = true;
}  

public static class Utility
{
    private static TaskCompletionSource<bool> tcs;
    private static Point _point = new Point();

    public static async Task<Point> PickPoint(Grid grid)
    {
        tcs = new TaskCompletionSource<bool>();
        _point = new Point();

        grid.MouseLeftButtonUp += GridOnMouseLeftButtonUp;


        await tcs.Task;

        grid.MouseLeftButtonUp -= GridOnMouseLeftButtonUp;
        return _point;
    }


    private static void GridOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {

        // do something here ....
        _point = new Point { X = 23, Y = 34 };
        // do something here ....

        tcs.SetResult(true); // as soon its set it will go back

    }
}

@ धन्यवाद, यह वही उत्तर है जो मुझे मेरे अन्य प्रश्न के लिए मिला है, जो async / प्रतीक्षा का उपयोग करता है।
वाहिद

अरे हाँ ! मैं इसे अब देखा है, लेकिन मुझे लगता है कि यह एकमात्र तरीका है जो मैंने पाया है कि काम कर रहा है
राव हमास हुसैन

2

आप एसिंक्रोनस रूप से एक का उपयोग कर ब्लॉक कर सकते हैं SemaphoreSlim:

public partial class MainWindow : Window, IDisposable
{
    private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(0, 1);

    public MainWindow()
    {
        InitializeComponent();
    }

    private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var point = Utility.PickPoint(View);

        // do not continue the code flow until the user has clicked on the grid. 
        // so when we debug, the code flow will literally stop here.
        await _semaphoreSlim.WaitAsync();

        MessageBox.Show(point.ToString());
    }

    private void View_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        //click on grid detected....
        _semaphoreSlim.Release();
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);
        Dispose();
    }

    public void Dispose() => _semaphoreSlim.Dispose();
}

आप नहीं कर सकते हैं, और आप या तो डिस्पैचर थ्रेड को सिंक्रोनाइज़ नहीं करना चाहते हैं क्योंकि तब वह कभी भी क्लिक को हैंडल नहीं कर पाएगा Grid, यानी यह एक ही समय में ब्लॉक और इवेंट दोनों को हैंडल नहीं कर सकता है।


वैकल्पिक उत्तर के लिए धन्यवाद। मुझे आश्चर्य है कि यह Console.Readline () में उदाहरण के लिए कैसे किया जाता है? जब आप डीबगर में इस विधि तक पहुँचते हैं, तो यह जादुई रूप से वहाँ रुक जाता है जब तक हम कुछ दर्ज नहीं करते हैं? क्या यह सांत्वना अनुप्रयोगों में मौलिक रूप से भिन्न है? क्या हम WinForms / WPF एप्लिकेशन में समान व्यवहार नहीं कर सकते? मैंने इसे ऑटोडेस्क रेविट के एपीआई में देखा है, वहां एक पिकपॉइंट () विधि है जो आपको स्क्रीन पर एक बिंदु चुनने के लिए मजबूर करती है और मैंने किसी भी एस्किंट / वेट का उपयोग नहीं देखा है! क्या कम से कम प्रतीक्षारत खोजशब्द को छिपाना और किसी सिंक विधि से किसी तरह कॉल करना संभव है?
वाहिद

@ वाहिद: Console.Readline ब्लॉक , यानी यह तब तक वापस नहीं आता जब तक कि एक पंक्ति को पढ़ा नहीं गया। आपका PickPointतरीका नहीं है। यह तुरंत लौटता है। यह संभावित रूप से अवरुद्ध हो सकता है लेकिन तब आप इस बीच यूआई इनपुट को संभालने में सक्षम नहीं होंगे जैसा कि मैंने अपने उत्तर में लिखा था। दूसरे शब्दों में, आपको समान व्यवहार प्राप्त करने के लिए विधि के अंदर क्लिक को संभालना होगा।
एमएम 8

Console.Realine () ब्लॉक लेकिन उसी समय KeyPress ईवेंट की अनुमति है। क्या हम यहाँ ठीक वैसा ही व्यवहार नहीं कर सकते? पिकपॉइंट द्वारा अवरुद्ध () और केवल माउसइवेंट्स की अनुमति है? मैं समझ नहीं पा रहा हूं कि यह कंसोल में क्यों संभव है लेकिन UI आधारित एप्लिकेशन पर नहीं।
वाहिद

फिर आपको एक अलग डिस्पैचर सेटअप करने की आवश्यकता PickPointहै जो माउस घटनाओं को संभालता है। मैं यह देखने में विफल हूं कि आप इसके साथ कहां जा रहे हैं?
एमएम 8

1
@Vahind: कोड को एसिंक्स बनाएं और उपयोगकर्ता को तब विधि का इंतजार करने दें? यह एपीआई है जो मैं यूआई डेवलपर के रूप में उम्मीद करूंगा। UI एप्लिकेशन में ब्लॉकिंग विधि को कॉल करने का कोई मतलब नहीं है।
एमएम 8

2

तकनीकी रूप से यह AutoResetEventऔर इसके बिना संभव है async/await, लेकिन एक महत्वपूर्ण कमी है:

public static Point PickPoint(Grid grid)
{
    var pointPicked = new AutoResetEvent(false);
    grid.MouseLeftButtonUp += (s, e) => 
    {
        // do whatever after the grid is clicked

        // signal the end of waiting
        pointPicked.Set();
    };

    // code flow will stop here and wait until the grid is clicked
    pointPicked.WaitOne();
    // return something...
}

खामी: यदि आप इस विधि को सीधे बटन ईवेंट हैंडलर में कहते हैं जैसा कि आपका नमूना कोड करता है, गतिरोध उत्पन्न होगा और आप देखेंगे कि एप्लिकेशन प्रतिक्रिया करना बंद कर देता है। क्योंकि आप उपयोगकर्ता के क्लिक की प्रतीक्षा के लिए केवल UI थ्रेड का उपयोग कर रहे हैं, यह ग्रिड पर उपयोगकर्ता के क्लिक सहित किसी भी उपयोगकर्ता की कार्रवाई का जवाब नहीं दे सकता है।

गतिरोध को रोकने के लिए विधि के उपभोक्ताओं को इसे दूसरे धागे में लागू करना चाहिए। यदि इसकी गारंटी दी जा सकती है, तो यह ठीक है। अन्यथा, आपको इस तरह विधि लागू करने की आवश्यकता है:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    // here I used ThreadPool, but you may use other means to run on another thread
    ThreadPool.QueueUserWorkItem(new WaitCallback(Capture));
}

private void Capture(object state)
{
    // do not continue the code flow until the user has clicked on the grid. 
    // so when we debug, the code flow will literally stop here.
    var point = Utility.PickPoint(View);


    MessageBox.Show(point.ToString());
}

यह आपके एपीआई के उपभोक्ताओं के लिए और अधिक परेशानी का कारण हो सकता है, सिवाय इसके कि वे अपने स्वयं के धागे का प्रबंधन करते थे। इसीलिए async/awaitआविष्कार किया गया था।


धन्यवाद केन, क्या यह संभव है कि एडिन दूसरे धागे से शुरू होता है और फिर इसकी घटनाएं मुख्य UI थ्रेड को ब्लॉक नहीं करती हैं?
वाहिद

@ वाहिद हां और ना। हां, आप ब्लॉकिंग विधि को किसी अन्य थ्रेड में कॉल कर सकते हैं और इसे किसी अन्य विधि में लपेट सकते हैं। हालाँकि, UI अवरोधन से बचने के लिए आवरण विधि को अभी भी UI थ्रेड के अलावा किसी अन्य थ्रेड में कॉल करने की आवश्यकता है। क्योंकि रैपर कॉलिंग थ्रेड को ब्लॉक करेगा यदि वह सिंक्रोनस है । हालांकि आंतरिक रूप से आवरण एक और धागे को अवरुद्ध करता है, फिर भी इसे परिणाम की प्रतीक्षा करने की आवश्यकता होती है और कॉलिंग थ्रेड को ब्लॉक करता है। यदि कॉलर यूआई थ्रेड में आवरण विधि को कॉल करता है, तो UI अवरुद्ध है।
केन हंग

0

मुझे लगता है कि मुद्दा डिजाइन पर ही है। यदि आपका एपीआई किसी विशेष तत्व पर काम करता है तो इसका उपयोग इस तत्व के इवेंट हैंडलर में किया जाना चाहिए, अन्य तत्व पर नहीं।

उदाहरण के लिए, यहां हम ग्रिड पर क्लिक इवेंट की स्थिति प्राप्त करना चाहते हैं, एपीआई को ग्रिड तत्व पर इवेंट से जुड़े इवेंट हैंडलर में उपयोग करने की आवश्यकता है, बटन तत्व पर नहीं।

अब, यदि आवश्यकता है कि हम बटन पर क्लिक करने के बाद ही ग्रिड पर क्लिक करें, तो बटन जिम्मेदारी ग्रिड पर ईवेंट हैंडलर जोड़ने के लिए होगी, और ग्रिड पर क्लिक ईवेंट संदेश बॉक्स को दिखाएगा और हटा देगा यह ईवेंट हैंडलर बटन द्वारा जोड़ा गया है ताकि इस क्लिक के बाद यह ट्रिगर न हो ... (UI थ्रेड को ब्लॉक करने की आवश्यकता नहीं है)

केवल यह कहने के लिए कि यदि आप बटन पर UI थ्रेड को ब्लॉक करते हैं, तो मुझे नहीं लगता कि UI थ्रेड बाद में ग्रिड पर क्लिक इवेंट को ट्रिगर करने में सक्षम होगा।


0

सबसे पहले, यूआई थ्रेड को ब्लॉक नहीं किया जा सकता है जैसे कि आपके शुरुआती प्रश्न से उत्तर मिला था।
यदि आप इससे सहमत हो सकते हैं, तो अपने ग्राहक को कम संशोधन करने के लिए async / प्रतीक्षा से बचना उचित है, और यहां तक ​​कि किसी भी मल्टी-थ्रेडिंग की आवश्यकता नहीं है।

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        Utility.PickPoint(View, (x) => MessageBox.Show(x.ToString()));
    }
}

public static class Utility
{
    private static Action<Point> work;

    public static void PickPoint(Grid grid, Action<Point> work)
    {
        if (Utility.work == null)
        {
            grid.PreviewMouseLeftButtonUp += Grid_PreviewMouseLeftButtonUp;
            Utility.work = work;
        }
    }

    private static void Grid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        var grid = (Grid)sender;
        work.Invoke(e.GetPosition(grid));
        grid.PreviewMouseLeftButtonUp -= Grid_PreviewMouseLeftButtonUp;
        Utility.work = null;
    }
}   

लेकिन अगर आप UI थ्रेड या "कोड फ्लो" को ब्लॉक करना चाहते हैं, तो इसका उत्तर यह होगा कि यह असंभव है। क्योंकि यदि UI थ्रेड ब्लॉक किया गया था, तो कोई और इनपुट नहीं मिल सकता है।
चूंकि आपने कंसोल ऐप के बारे में बताया है, इसलिए मैं कुछ सरल स्पष्टीकरण देता हूं।
जब आप एक कंसोल ऐप या कॉल चलाते हैंAllocConsole ऐसी प्रक्रिया से करते हैं जो किसी कंसोल (विंडो) से जुड़ी नहीं होती है, तो conhost.exe जो एक कंसोल (विंडो) प्रदान कर सकती है, निष्पादित होगी और कंसोल ऐप या कॉलर प्रक्रिया कंसोल से जुड़ी होगी ( खिड़की)।
तो आपके द्वारा लिखा गया कोई भी कोड जो कॉलर थ्रेड को ब्लॉक कर सकता है जैसे Console.ReadKeyकंसोल विंडो के UI थ्रेड को ब्लॉक नहीं करेगा, यही कारण है कि जब कंसोल ऐप आपके इनपुट की प्रतीक्षा कर रहा है लेकिन फिर भी माउस क्लिक करने जैसे अन्य इनपुट का जवाब दे सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.