c# - क्या किसी अन्य एसिंक विधि की बजाय किसी ईवेंट का इंतजार करना संभव है?




microsoft-metro .net-4.5 (5)

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

उस कैनोलिक उदाहरण पर विचार करें जहां उपयोगकर्ता लंच पर जाता है जबकि बटन क्लिक करने की प्रतीक्षा कर रहा है।

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

उस ने कहा, यदि आपके एसिंक्रोनस ऑपरेशन में बेहतर है, तो आप इस स्थिति को सेट करते हैं कि आपको उस बिंदु पर बनाए रखने की आवश्यकता है जहां बटन सक्षम है और आप एक क्लिक पर "प्रतीक्षा" कर रहे हैं। उस समय, आपकी GetResults विधि बंद हो जाती है

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

चूंकि SynchronizationContext GetResults इवेंट हैंडलर में कैप्चर किया जाएगा जो GetResults कॉल GetResults (संकलक await किए जाने वाले await कीवर्ड का उपयोग करने के परिणामस्वरूप ऐसा करेगा, और तथ्य यह है कि SynchronizationContext.Current GetResults गैर-शून्य होना चाहिए, बशर्ते आप यूआई एप्लिकेशन में हों ), आप async/await कर सकते हैं जैसे:

private async void Button_Click_1(object sender, RoutedEventArgs e) 
{
     await GetResults();

     // Show dialog/UI element.  This code has been marshaled
     // back to the UI thread because the SynchronizationContext
     // was captured behind the scenes when
     // await was called on the previous line.
     ...

     // Check continue, if true, then continue with another async task.
     if (_continue) await ContinueToGetResultsAsync();
}

private bool _continue = false;
private void buttonContinue_Click(object sender, RoutedEventArgs e)
{
    _continue = true;
}

private async Task GetResults()
{ 
     // Do lot of complex stuff that takes a long time
     // (e.g. contact some web services)
  ...
}

ContinueToGetResultsAsync वह तरीका है जो आपके बटन को धक्का देने के दौरान परिणाम प्राप्त करना जारी रखता है। यदि आपका बटन धक्का नहीं दिया गया है, तो आपका ईवेंट हैंडलर कुछ भी नहीं करता है।

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

private async void Button_Click_1(object sender, RoutedEventArgs e) 
{
     await GetResults();
}

private async Task GetResults()
{ 
     // Do lot of complex stuff that takes a long time
     // (e.g. contact some web services)
  ...
}

कभी-कभी, GetResults के भीतर होने वाली सामग्री को जारी रखने से पहले अतिरिक्त उपयोगकर्ता इनपुट की आवश्यकता होगी। सादगी के लिए, मान लें कि उपयोगकर्ता को बस "जारी रखें" बटन पर क्लिक करना होगा।

मेरा सवाल है: मैं GetResults के निष्पादन को इस तरह से कैसे निलंबित कर सकता हूं कि यह किसी अन्य बटन के क्लिक जैसे किसी ईवेंट की प्रतीक्षा कर रहा है?

मैं जो खोज रहा हूं उसे प्राप्त करने के लिए यहां एक बदसूरत तरीका है: जारी रखने के लिए ईवेंट हैंडलर "बटन ध्वज सेट करता है ...

private bool _continue = false;
private void buttonContinue_Click(object sender, RoutedEventArgs e)
{
    _continue = true;
}

... और GetResults समय-समय पर इसे चुनाव:

 buttonContinue.Visibility = Visibility.Visible;
 while (!_continue) await Task.Delay(100);  // poll _continue every 100ms
 buttonContinue.Visibility = Visibility.Collapsed;

मतदान स्पष्ट रूप से भयानक है (व्यस्त इंतजार / चक्रों का अपशिष्ट) और मैं कुछ घटना-आधारित की तलाश में हूं।

कोई विचार?

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


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

private SemaphoreSlim signal = new SemaphoreSlim(0, 1);

// set signal in event
signal.Release();

// wait for signal somewhere else
await signal.WaitAsync();

वैकल्पिक रूप से, आप Task<T> को बनाने के लिए TaskCompletionSource <T> क्लास का एक उदाहरण <T> बनाने के लिए उपयोग कर सकते हैं जो बटन क्लिक के परिणाम का प्रतिनिधित्व करता है:

private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

// complete task in event
tcs.SetResult(true);

// wait for task somewhere else
await tcs.Task;

यहां एक उपयोगिता वर्ग है जिसका मैं उपयोग करता हूं:

public class AsyncEventListener
{
    private readonly Func<bool> _predicate;

    public AsyncEventListener() : this(() => true)
    {

    }

    public AsyncEventListener(Func<bool> predicate)
    {
        _predicate = predicate;
        Successfully = new Task(() => { });
    }

    public void Listen(object sender, EventArgs eventArgs)
    {
        if (!Successfully.IsCompleted && _predicate.Invoke())
        {
            Successfully.RunSynchronously();
        }
    }

    public Task Successfully { get; }
}

और यहां मैं इसका उपयोग कैसे करता हूं:

var itChanged = new AsyncEventListener();
someObject.PropertyChanged += itChanged.Listen;

// ... make it change ...

await itChanged.Successfully;
someObject.PropertyChanged -= itChanged.Listen;

स्टीफन AsyncManualResetEvent ने अपने ब्लॉग पर इस AsyncManualResetEvent कक्षा को प्रकाशित किया।

public class AsyncManualResetEvent 
{ 
    private volatile TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>();

    public Task WaitAsync() { return m_tcs.Task; } 

    public void Set() 
    { 
        var tcs = m_tcs; 
        Task.Factory.StartNew(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), 
            tcs, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default); 
        tcs.Task.Wait(); 
    }

    public void Reset() 
    { 
        while (true) 
        { 
            var tcs = m_tcs; 
            if (!tcs.Task.IsCompleted || 
                Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource<bool>(), tcs) == tcs) 
                return; 
        } 
    } 
}

सरल सहायक वर्ग:

public class EventAwaiter<TEventArgs>
{
    private readonly TaskCompletionSource<TEventArgs> _eventArrived = new TaskCompletionSource<TEventArgs>();

    private readonly Action<EventHandler<TEventArgs>> _unsubscribe;

    public EventAwaiter(Action<EventHandler<TEventArgs>> subscribe, Action<EventHandler<TEventArgs>> unsubscribe)
    {
        subscribe(Subscription);
        _unsubscribe = unsubscribe;
    }

    public Task<TEventArgs> Task => _eventArrived.Task;

    private EventHandler<TEventArgs> Subscription => (s, e) =>
        {
            _eventArrived.TrySetResult(e);
            _unsubscribe(Subscription);
        };
}

उपयोग:

var valueChangedEventAwaiter = new EventAwaiter<YourEventArgs>(
                            h => example.YourEvent += h,
                            h => example.YourEvent -= h);
await valueChangedEventAwaiter.Task;




windows-store-apps