मैं एक async टास्क <T> विधि को सिंक्रोनाइज़ कैसे करूँगा?


628

मैं async / प्रतीक्षा के बारे में सीख रहा हूँ, और ऐसी स्थिति में भाग गया जहाँ मुझे एक async विधि को सिंक्रोनाइज़ करने की आवश्यकता है। मैं उसे कैसे कर सकता हूँ?

Async विधि:

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}

सामान्य उपयोग:

public async void GetCustomers()
{
    customerList = await GetCustomers();
}

मैंने निम्नलिखित का उपयोग करने की कोशिश की है:

Task<Customer> task = GetCustomers();
task.Wait()

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)

मैंने भी यहाँ से एक सुझाव की कोशिश की , हालाँकि यह तब काम नहीं करता जब डिस्पैचर एक निलंबित स्थिति में होता है।

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

यहाँ कॉलिंग से अपवाद और स्टैक ट्रेस है RunSynchronously:

System.InvalidOperationException

संदेश : RunSynchronously एक प्रतिनिधि को अनबाउंड कार्य पर नहीं बुलाया जा सकता है।

इनर एक्ससेप्शन : अशक्त

स्रोत : mscorlib

StackTrace :

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

46
सवाल का सबसे अच्छा जवाब "मैं कैसे एक async विधि को सिंक्रोनाइज़ कर सकता हूं" यह "नहीं" है। इसे काम करने के लिए मजबूर करने की कोशिश करने के लिए हैक हैं , लेकिन उन सभी में बहुत सूक्ष्म नुकसान हैं। इसके बजाय, ऐसा करने के लिए "आवश्यकता" बनाने वाले कोड का बैकअप लें और ठीक करें।
स्टीफन क्लीयर

57
@ स्टेफ़ेन क्लीरी बिल्कुल सहमत हैं, लेकिन कभी-कभी इसका बस अपरिहार्य है, जैसे कि जब आपका कोड कुछ 3 पार्टी एपीआई पर निर्भर होता है जो कि एसिंक्स / वेट का उपयोग नहीं करता है। इसके अलावा, अगर MVVM का उपयोग करते समय WPF संपत्तियों के लिए बाध्य किया जाता है, तो इसका वस्तुतः असंभव है क्योंकि यह गुणों पर समर्थित नहीं है।
कंटैंगो

3
@StephenCleary हमेशा नहीं। मैं एक DLL का निर्माण कर रहा हूं, जिसे GeneXus में आयात किया जाएगा । यह एसिंक्स / वेट कीवर्ड का समर्थन नहीं करता है, इसलिए मुझे केवल सिंक्रोनस विधियों का उपयोग करना चाहिए।
दीनी

5
@StephenCleary 1) GeneXus एक 3rd pt टूल है और मेरे पास इसके सोर्स कोड तक पहुंच नहीं है; 2) जिनेक्स के पास "फ़ंक्शन" का कार्यान्वयन भी नहीं है, इसलिए मुझे एहसास नहीं हो सकता है कि मैं इस प्रकार की चीज़ के साथ "कॉलबैक" कैसे लागू कर सकता हूं। निश्चित रूप से यह Taskतुल्यकालिक का उपयोग करने की तुलना में एक कठिन समाधान होगा ; 3) मैं MongoDB C # ड्राइवर के साथ GeneXus को एकीकृत कर रहा हूं , जो कुछ तरीकों को केवल अतुल्यकालिक रूप से उजागर करता है
Dinei

1
@ygoe: जैसे कि एक async- संगत लॉक का उपयोग करें SemaphoreSlim
स्टीफन क्लीयर

जवाबों:


455

यहाँ मैंने पाया है कि सभी मामलों के लिए काम करता है (निलंबित डिस्पैचर सहित)। यह मेरा कोड नहीं है और मैं अभी भी इसे पूरी तरह से समझने के लिए काम कर रहा हूं, लेकिन यह काम करता है।

इसे उपयोग करके बुलाया जा सकता है:

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

कोड यहाँ से है

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }
}

28
यह कैसे काम करता है, इस पर कुछ पृष्ठभूमि के लिए, स्टीफन टूब (श्री समानांतर) ने इस बारे में पोस्ट की एक श्रृंखला लिखी। भाग 1 भाग 2 भाग 3
कैमरून मैकफारलैंड

18
मैंने जॉन के कोड को लैंबडास में कार्यों को लपेटे बिना काम करने के लिए अद्यतन किया: github.com/tejacques/AsyncBridge । अनिवार्य रूप से आप उपयोग कथन के साथ async ब्लॉक के साथ काम करते हैं। एक उपयोग ब्लॉक के अंदर कुछ भी अतुल्यकालिक रूप से होता है, अंत में एक प्रतीक्षा के साथ। नकारात्मक पक्ष यह है कि आपको कॉलबैक में अपने आप को कार्य को अनफ्रेंड करने की आवश्यकता है, लेकिन यह अभी भी काफी सुरुचिपूर्ण है, खासकर यदि आपको एक बार में कई एसिंक्स फ़ंक्शन कॉल करने की आवश्यकता है।
टॉम जेक्स

17
@StephenCleary हालांकि मैं आमतौर पर आपके साथ सहमत हूं कि कोड सभी तरह से नीचे होना चाहिए, कभी-कभी आप अपने आप को एक अनम्य स्थिति में पाते हैं जहां किसी को इसे एक तुल्यकालिक कॉल के रूप में मजबूर करना पड़ता है। मूल रूप से, मेरी स्थिति यह है कि मेरे सभी डेटा एक्सेस कोड async फैशन में हैं। मुझे साइटमैप के आधार पर साइटमैप बनाने की आवश्यकता थी और मैं जिस थर्ड पार्टी लाइब्रेरी का उपयोग कर रहा था वह MvcSitemap थी। अब जब कोई इसे DynamicNodeProviderBaseआधार वर्ग के माध्यम से बढ़ा रहा है , तो कोई इसे asyncविधि के रूप में घोषित नहीं कर सकता है । या तो मुझे एक नई लाइब्रेरी से बदलना था, या बस एक सिंक्रोनस ऑप को कॉल करना था।
justin.lovell

6
@ justin.lovell: हां, लाइब्रेरी की सीमाएं हमें हैक में डालने के लिए मजबूर कर सकती हैं, कम से कम तब तक जब तक लाइब्रेरी अपडेट न हो जाए। ऐसा लगता है कि MvcSitemap एक ऐसी स्थिति है जहां एक हैक की आवश्यकता होती है (MVC फ़िल्टर और बच्चे की क्रियाएं, भी); मैं बस लोगों को सामान्य रूप से इससे मना करता हूं क्योंकि इस तरह के हैक का उपयोग बहुत बार किया जाता है जब वे आवश्यक नहीं होते हैं । विशेष रूप से MVC के साथ, कुछ ASP.NET/MVC API यह मानते हैं कि उनके पास एक है AspNetSynchronizationContext, इसलिए यदि आप अपने API को कॉल कर रहे हैं तो यह विशेष हैक काम नहीं करेगा।
स्टीफन क्लीयर

5
यह कोड काम नहीं करेगा। यदि इसे पूल थ्रेड से बुलाया जाता है तो यह थ्रेड-भुखमरी गतिरोध को गति प्रदान कर सकता है। आपका कॉलर ऑपरेशन के पूरा होने की प्रतीक्षा कर रहा है, जो कभी नहीं हो सकता है यदि उसने थ्रेड पूल को समाप्त कर दिया है। इस लेख को देखें ।
ZunTzu

318

सलाह है कि यह उत्तर तीन साल पुराना है। मैंने इसे ज्यादातर .net 4.0 के साथ एक अनुभव के आधार पर लिखा था, और विशेष रूप से 4.5 के साथ बहुत कम async-await। सामान्यतया यह एक अच्छा सरल उपाय है, लेकिन यह कभी-कभी चीजों को तोड़ देता है। कृपया टिप्पणियों में चर्चा पढ़ें।

.नेट 4.5

बस इस का उपयोग करें:

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

देखें: TaskAwaiter , Task.Result , Task.RunSynchronously


.नेट 4.0

इसे इस्तेमाल करो:

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

...या यह:

task.Start();
task.Wait();

67
.Resultकुछ परिदृश्य में एक गतिरोध उत्पन्न कर सकता है
जॉर्डन लैंगेन

122
Resultआसानी से asyncकोड में गतिरोध पैदा कर सकता है , जैसा कि मैं अपने ब्लॉग पर वर्णन करता हूं।
स्टीफन क्लीयर

8
@StephenCleary मैंने आपकी पोस्ट पढ़ी, और स्वयं इसे आज़माया। मुझे ईमानदारी से लगता है कि Microsoft में कोई वास्तव में नशे में था ... यह winforms और पृष्ठभूमि थ्रेड्स की तरह ही मुद्दा है ....
AK_

9
प्रश्न async विधि द्वारा लौटाए गए कार्य की चिंता करता है। इस तरह के टास्क को पहले ही शुरू किया जा सकता है, निष्पादित या रद्द किया जा सकता है, इसलिए Task.RunSynchronously विधि के उपयोग के परिणामस्वरूप InvalidOperationException हो सकती है । MSDN पृष्ठ देखें: Task.RunSynchronously विधि । इसके अलावा, वह टास्क संभवतः Task.Factory.StartNew या Task.Run तरीकों (async विधि के अंदर) द्वारा बनाया गया है , इसलिए इसे फिर से शुरू करने की कोशिश करना खतरनाक है। दौड़ के समय कुछ दौड़ की स्थिति हो सकती है। Othe हाथ में, Task.Wait और Task.Result का परिणाम i deadlock हो सकता है।
sgnsajgon

4
मेरे लिए समान रूप से काम करें ... मुझे नहीं पता कि मुझे कुछ याद आ रहा है, लेकिन यह चिह्नित उत्तर की भयावहता के लिए बेहतर लगता है - मैं बस परीक्षण कोड के लिए async बंद करने का एक तरीका ढूंढ रहा था कि बस वहीं रुक जाए ui फांसी से
जोनीरा

121

आश्चर्यचकित किसी ने इसका उल्लेख नहीं किया:

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

यहाँ कुछ अन्य विधियों की तरह सुंदर नहीं है, लेकिन इसके निम्नलिखित लाभ हैं:

इसके अलावा, चूंकि GetAwaiterडक-टाइप किया गया है, यह किसी भी ऑब्जेक्ट के लिए काम करना चाहिए जो कि एक async विधि (जैसे ConfiguredAwaitableया YieldAwaitable) से वापस आ गया है , न कि केवल मास्क।


संपादित करें: कृपया ध्यान दें कि इस दृष्टिकोण के लिए (या उपयोग करना .Result) गतिरोध संभव है, जब तक कि आप प्रतीक्षा नहीं करते हैं कि आप .ConfigureAwait(false)हर समय का इंतजार करें, सभी async विधियों के लिए, जो संभवत: उस तक पहुंच सकते हैं BlahAsync()(न कि केवल उन लोगों को जो सीधे कॉल करते हैं)। स्पष्टीकरण

// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
                                        // all its descendants use ConfigureAwait(false)
                                        // too. Then you can be sure that
                                        // BlahAsync().GetAwaiter().GetResult()
                                        // won't deadlock.

यदि आप .ConfigureAwait(false)हर जगह जोड़ने के लिए बहुत आलसी हैं , और आप प्रदर्शन के बारे में परवाह नहीं करते हैं तो आप वैकल्पिक रूप से कर सकते हैं

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()

1
साधारण सामान के लिए मेरे लिए काम करता है। इसके अलावा, यदि विधि एक IAsyncOperation देता है, तो मुझे इसे पहले टास्क में बदलना होगा: BlahAsync (); AsTask ()। GetAwaiter ()। GetResult ();
ली मैकफर्सन

3
इसके कारण एक asmx वेब विधि के अंदर गतिरोध उत्पन्न हो गया। फिर भी, एक टास्क में विधि कॉल को लपेटकर। Run () ने यह काम किया: Task.Run () => BlahAsync ()) GetAwaiter ()। GetResult ()
Augusto Barreto

मैं इस दृष्टिकोण को सबसे अच्छा रूप से पसंद करता हूं क्योंकि इसमें लैम्ब्डा शामिल नहीं है।
dythim

24
कृपया अपनी खुद की लिंक डालने के लिए अन्य लोगों के उत्तरों को संपादित न करें। यदि आपको लगता है कि आपका उत्तर बेहतर है, तो इसे टिप्पणी के रूप में छोड़ दें।
रेचल

1
docs.microsoft.com/en-us/dotnet/api/... के बारे में कहते हैं GetAwaiter(), "यह पद्धति नहीं बल्कि कोड में सीधे उपयोग से संकलक उपयोगकर्ता के लिए है।"
थियोफिलस

75

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

Task<MyResult> DoSomethingAsync() { ... }

// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());

// Will block until the task is completed...
MyResult result = task.Result; 

3
तब आप कार्य को कॉल करते हैं। डेटा प्रकार बस कार्य है।
माइकल एल पेरी

1
चलिए मान लेते हैं कि DoSomethingAsync () लंबे समय से चलने वाली एस्किंक विधि है (आंतरिक रूप से यह एक लंबे समय से चलने वाले कार्य की प्रतीक्षा करता है), लेकिन यह जल्दी से अपने कॉलर को एक प्रवाह नियंत्रण देता है, इस प्रकार लैम्बडा तर्क कार्य भी जल्दी से समाप्त हो जाता है। Tusk.Run () का परिणाम टास्क <टास्क> या टास्क <टास्क <>> हो सकता है , इसलिए आप बाहरी कार्य के परिणाम की प्रतीक्षा कर रहे हैं जो जल्दी पूरा हो जाता है, लेकिन आंतरिक कार्य (एस्किंस विधि में लंबे समय तक चलने वाली नौकरी की प्रतीक्षा के कारण) अभी भी चल रहा है। निष्कर्ष यह है कि हमें संभवतः एसिंक्रियस व्यवहार के तुल्यकालिक व्यवहार को प्राप्त करने के लिए Unwrap () दृष्टिकोण (जैसा कि @ J.Lennon पोस्ट में किया गया था ) का उपयोग करने की आवश्यकता है ।
sagnsajgon

5
@sgnsajgon आप गलत हैं। Task.Run Task.Factory.StartNew से अलग है कि यह स्वचालित रूप से परिणाम को पहले से ही खोल देता है। इस लेख को देखें ।
ZunTzu

1
क्या मैं Task.Run(DoSomethingAsync)इसके बजाय लिख सकता हूँ ? यह प्रतिनिधियों के एक स्तर को हटा देता है।
यज्ञ डे

1
हां। विपरीत दिशा में जा रहा है, हालांकि, जैसा Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());कि अधिक स्पष्ट है और @sgnsajgon द्वारा चिंता को संबोधित करता है कि यह एक टास्क <टास्क <MyResult >> लौटा सकता है। Task.Run का सही अधिभार वैसे तो चुना जाता है, लेकिन Async प्रतिनिधि आपके इरादे को स्पष्ट करता है।
माइकल एल पेरी

57

मैं async / प्रतीक्षा के बारे में सीख रहा हूँ, और ऐसी स्थिति में भाग गया जहाँ मुझे एक async विधि को सिंक्रोनाइज़ करने की आवश्यकता है। मैं उसे कैसे कर सकता हूँ?

सबसे अच्छा जवाब आप "स्थिति" क्या है पर निर्भर विवरण के साथ नहीं है।

क्या यह एक संपत्ति पाने वाला / सेटर है? ज्यादातर मामलों में, "अतुल्यकालिक गुणों" की तुलना में अतुल्यकालिक तरीकों का होना बेहतर है। (अधिक जानकारी के लिए, अतुल्यकालिक गुणों पर मेरा ब्लॉग पोस्ट देखें )।

क्या यह MVVM ऐप है और आप एसिंक्रोनस डेटा बाइंडिंग करना चाहते हैं? फिर मेरे जैसे कुछ का उपयोग करें NotifyTask, जैसा कि एसिंक्रोनस डेटा बाइंडिंग पर मेरे एमएसडीएन लेख में वर्णित है ।

क्या यह कंस्ट्रक्टर है? तब आप शायद एक अतुल्यकालिक कारखाना विधि पर विचार करना चाहते हैं। (अधिक जानकारी के लिए, अतुल्यकालिक निर्माणकर्ताओं पर मेरा ब्लॉग पोस्ट देखें )।

सिंक-ओवर-एसिंक्स करने की तुलना में लगभग हमेशा बेहतर उत्तर होता है।

यदि यह आपकी स्थिति के लिए संभव नहीं है (और आप स्थिति का वर्णन करते हुए यहां एक प्रश्न पूछकर इसे जानते हैं ), तो मैं केवल तुल्यकालिक कोड का उपयोग करने की सलाह दूंगा। सभी तरह से Async सबसे अच्छा है; सभी तरह से सिंक करना सबसे अच्छा है। सिंक-ओवर-एसिक्स की अनुशंसा नहीं की जाती है।

हालाँकि, कुछ मुट्ठी भर परिस्थितियाँ ऐसी होती हैं, जिनमें सिंक-ओवर-एसिंक्स आवश्यक है। विशेष रूप से, आप बुला कोड से विवश कर रहे ताकि आप उस राशि सिंक होने के लिए (और करने के लिए पूरी तरह से कोई तरीका नहीं है फिर से लगता है या फिर से संरचना अपने कोड asynchrony अनुमति देने के लिए), और आप है कोड async कॉल करने के लिए। यह एक बहुत ही दुर्लभ स्थिति है, लेकिन यह समय-समय पर सामने आती है।

उस स्थिति में, आपको ब्राउनफील्ड asyncविकास पर मेरे लेख में वर्णित हैक में से एक का उपयोग करने की आवश्यकता होगी , विशेष रूप से:

  • अवरुद्ध करना (जैसे, GetAwaiter().GetResult())। ध्यान दें कि यह गतिरोध पैदा कर सकता है (जैसा कि मैं अपने ब्लॉग पर वर्णन करता हूं)।
  • एक थ्रेड पूल थ्रेड (जैसे, Task.Run(..).GetAwaiter().GetResult()) पर कोड चलाना । ध्यान दें कि यह केवल तभी काम करेगा जब एसिंक्रोनस कोड थ्रेड पूल थ्रेड पर चलाया जा सकता है (यानी, UI या ASP.NET संदर्भ पर निर्भर नहीं है)।
  • नेस्टेड संदेश लूप्स। ध्यान दें कि यह केवल तभी काम करेगा जब एसिंक्रोनस कोड केवल एक-थ्रेडेड संदर्भ मानता है, न कि एक विशिष्ट संदर्भ प्रकार (बहुत सारे यूआई और एएसपी .नेट कोड एक विशिष्ट संदर्भ की अपेक्षा करते हैं)।

नेस्टेड मैसेज लूप्स सभी हैक्स के सबसे खतरनाक होते हैं, क्योंकि यह फिर से प्रवेश का कारण बनता है । री-एंट्रेंस के बारे में तर्क करना बेहद मुश्किल है, और (IMO) विंडोज पर अधिकांश एप्लिकेशन बग का कारण है। विशेष रूप से, यदि आप यूआई थ्रेड पर हैं और आप एक कार्य कतार पर हैं (async कार्य पूर्ण होने की प्रतीक्षा कर रहे हैं), तो CLR वास्तव में आपके लिए कुछ संदेश पम्पिंग करता है - यह वास्तव में आपके भीतर से कुछ Win32 संदेशों को संभालेगा कोड । ओह, और आपको पता नहीं है कि कौन से संदेश हैं - जब क्रिस ब्रुम कहते हैं "क्या यह जानना बहुत अच्छा नहीं होगा कि वास्तव में क्या पंप मिलेगा? दुर्भाग्य से, पंपिंग एक काली कला है जो नश्वर समझ से परे है।" , तो हम वास्तव में जानने की कोई उम्मीद नहीं है।

इसलिए, जब आप UI थ्रेड पर इस तरह से ब्लॉक करते हैं, तो आप परेशानी पूछ रहे हैं। एक ही लेख से एक और cbrumme उद्धरण: "समय-समय पर, कंपनी के अंदर या बाहर के ग्राहकों को पता चलता है कि हम STA [UI थ्रेड] पर प्रबंधित अवरोधन के दौरान संदेश पंप कर रहे हैं। यह एक वैध चिंता का विषय है, क्योंकि वे जानते हैं कि यह बहुत कठिन है। कोड लिखने के लिए जो पुनरावृत्ति की स्थिति में मजबूत है। "

हाँ यही है। कोड लिखने के लिए बहुत कठिन है जो पुनर्संयोजन के मामले में मजबूत है। और नेस्टेड संदेश लूप्स आपको कोड लिखने के लिए मजबूर करता है जो कि रीटर्रेंसी के मामले में मजबूत है। यही कारण है कि है स्वीकार किए जाते हैं (और सबसे-upvoted) इस प्रश्न के लिए जवाब है बेहद खतरनाक व्यवहार में।

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

यदि आप अपने आप को इस कोने में पाते हैं, तो मैं Dispatcher.PushFrameWPF ऐप्स के लिए कुछ का उपयोग करने की सलाह Application.DoEventsदूंगा, WinForm ऐप्स के साथ लूपिंग , और सामान्य स्थिति के लिए, मेरा अपना AsyncContext.Run


स्टीफन, इसी तरह का एक और क़िस्सा है जो आपने ज़बरदस्त जवाब भी दिया है। क्या आपको लगता है कि उनमें से एक को डुप्लिकेट के रूप में बंद किया जा सकता है या शायद अनुरोध को मर्ज कर सकता है या पहले मेटा पर ला सकता है (जैसा कि प्रत्येक q में ~ 200K के विचार 200+ वोट हैं)? सुझाव?
अलेक्सई लेवेनकोव

1
@AlexeiLevenkov: मुझे कुछ कारणों से, ऐसा करना सही नहीं लगता: 1) लिंक किए गए प्रश्न का उत्तर काफी पुराना है। 2) मैंने इस विषय पर एक पूरा लेख लिखा है जो मुझे लगता है कि किसी भी मौजूदा एसओ क्यू / ए की तुलना में अधिक पूर्ण है। 3) इस सवाल पर स्वीकृत जवाब बेहद लोकप्रिय है। 4) मैं उस स्वीकृत जवाब का घोर विरोध करता हूं । इसलिए, इसे एक डुबकी के रूप में बंद करना शक्ति का दुरुपयोग होगा; इसे बंद करने से (या विलय के) एक खतरनाक उत्तर और भी अधिक सशक्त होगा। मैंने इसे होने दिया, और इसे समुदाय पर छोड़ दिया।
स्टीफन क्लीयर

ठीक है। मैं इसे मेटा पर किसी तरह से लाने पर विचार करूंगा।
अलेक्सई लेवेनकोव

9
यह जवाब मेरे सिर के ऊपर से बहुत दूर तक जाता है। स्पष्ट रूप से पालन करने के लिए संभव नहीं होने के कारण, "सभी प्रकार के एस्किंक का उपयोग करें" सलाह को भ्रमित कर रहा है। एक async Main()विधि के साथ एक कार्यक्रम संकलित नहीं करता है; कुछ बिंदु पर आप गए हैं मिल गया सिंक और async दुनियाओं के बीच की खाई को पाटने के लिए। यह एक " बहुत दुर्लभ स्थिति" नहीं है , यह वास्तव में हर कार्यक्रम में आवश्यक है जो एक async विधि को कॉल करता है। "सिंक-ओवर-एसिंक्स" न करने का कोई विकल्प नहीं है , बस उस विकल्प को कॉल करने के तरीके पर बोझ डालना है जो आप वर्तमान में लिख रहे हैं।
मार्क अमेरी

1
महान। मैं asyncअपने आवेदन में अब सभी विधियों को डालने वाला हूँ । और वह बहुत कुछ है। क्या यह सिर्फ डिफ़ॉल्ट नहीं हो सकता?
योगो

25

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

C # 5 में Async विधियों को हुड के तहत टुकड़ों में विधि को प्रभावी ढंग से काटकर संचालित किया जाता है, और Taskजो पूरे शबांग के समग्र समापन को ट्रैक कर सकता है। हालांकि, कटा हुआ तरीके कैसे निष्पादित करते हैं, यह awaitऑपरेटर को दिए गए अभिव्यक्ति के प्रकार पर निर्भर कर सकता है ।

अधिकांश समय, आप awaitएक प्रकार की अभिव्यक्ति का उपयोग करेंगे Task। टास्क का awaitपरिपाटी का क्रियान्वयन "स्मार्ट" है, क्योंकि यह SynchronizationContextमूल रूप से निम्न का कारण बनता है:

  1. यदि प्रवेश करने वाला धागा awaitडिस्पैचर या WinForms संदेश लूप थ्रेड पर है, तो यह सुनिश्चित करता है कि संदेश कतार के प्रसंस्करण के भाग के रूप में async विधि का हिस्सा होता है।
  2. यदि थ्रेड में प्रवेश करने awaitवाला धागा थ्रेड पूल थ्रेड पर है, तो एसिंक्स विधि का शेष हिस्सा थ्रेड पूल पर कहीं भी होता है।

इसीलिए आप शायद समस्याओं में भाग रहे हैं - Async विधि कार्यान्वयन शेष को डिस्पैचर पर चलाने की कोशिश कर रहा है - भले ही वह निलंबित हो।

.... समर्थन करना! ....

मुझे यह सवाल पूछना है कि आप एसिंक्रोनस पद्धति पर सिंक्रोनाइज़ करने की कोशिश क्यों कर रहे हैं? ऐसा करने से इस उद्देश्य की हार होगी कि विधि को अतुल्यकालिक क्यों कहा जाना चाहिए। सामान्य तौर पर, जब आप awaitएक डिस्पैचर या यूआई विधि का उपयोग करना शुरू करते हैं, तो आप अपने पूरे यूआई प्रवाह को एसिंक्स चालू करना चाहेंगे। उदाहरण के लिए, यदि आपका कॉलस्टैक कुछ इस तरह था:

  1. [ऊपर] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()- WPFया WinFormsकोड
  6. [संदेश लूप] - WPFया WinFormsसंदेश लूप

फिर एक बार कोड को एसिंक्स का उपयोग करने के लिए बदल दिया गया है, तो आप आमतौर पर समाप्त हो जाएंगे

  1. [ऊपर] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()- WPFया WinFormsकोड
  6. [संदेश लूप] - WPFया WinFormsसंदेश लूप

वास्तव में उत्तर दे रहा है

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

एक और वर्कअराउंड थ्रेडपूल थ्रेड पर अपने async विधि को निष्पादित करना है, और फिर इसे पूरा करने के लिए प्रतीक्षा करें। ऐसा करना आसान है - आप इसे निम्नलिखित स्निपेट के साथ कर सकते हैं:

var customerList = TaskEx.RunEx(GetCustomers).Result;

अंतिम API टास्क होगा। Run (...), लेकिन CTP के साथ आपको Ex प्रत्यय ( यहाँ स्पष्टीकरण ) की आवश्यकता होगी ।


विस्तृत विवरण के लिए +1, हालांकि, TaskEx.RunEx(GetCustomers).Resultएक निलंबित डिस्पैचर थ्रेड पर चलने पर एप्लिकेशन को लटका देता है। इसके अलावा, GetCustomers () विधि आम तौर पर async चलाया जाता है, हालांकि एक स्थिति में इसे सिंक्रोनस रूप से चलाने की आवश्यकता होती है, इसलिए मैं ऐसा करने के लिए एक तरीका ढूंढ रहा था जो बिना विधि के सिंक संस्करण का निर्माण किए।
राहेल

+1 के लिए "आप एक async विधि पर सिंक्रोनाइज़ करने की कोशिश क्यों कर रहे हैं?" हमेशा asyncतरीकों का ठीक से उपयोग करने का एक तरीका है ; नेस्टेड छोरों से निश्चित रूप से बचा जाना चाहिए।
स्टीफन क्लीयर

24

यह मेरे लिए अच्छा काम कर रहा है

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}

आपको Task.Unwrap पद्धति का भी उपयोग करने की आवश्यकता है , क्योंकि आपका Task.Wait स्टेटमेंट बाहरी टास्क के लिए प्रतीक्षा करता है ( Task.Run द्वारा बनाया गया है ), इनर वेट के लिए नहीं t टास्क एक्सटेंशन विधि के पैरामीटर के रूप में पारित किया गया है। आपका Task.Run मेथड टास्क <T> नहीं देता है, लेकिन टास्क <टास्क <T >> है। कुछ सरल परिदृश्यों में आपका समाधान कार्य के कारण कार्य कर सकता है, उदाहरण के लिए, TryExecuteTaskInline विधि का उपयोग करके, उदाहरण के लिए, वर्तमान ऑपरेशन के दौरान कार्य निष्पादित करने के लिए प्रतीक्षा करें कार्रवाई के दौरान। कृपया इस उत्तर के लिए मेरी टिप्पणी देखें ।
sgnsajgon

1
वह सही नहीं है। टास्क.रुन टास्क <टी> वापस कर देगा। इस अधिभार को देखें msdn.microsoft.com/en-us/library/hh194918(v=vs.110).aspx
क्लेमेंट

इसका उपयोग कैसे किया जाना चाहिए? WPF में यह गतिरोध:MyAsyncMethod().RunTaskSynchronously();
ygoe

18

सबसे आसान तरीका है कि मैंने कार्य को सिंक्रोनाइज़ करने के लिए पाया है और UI थ्रेड को ब्लॉक किए बिना RunSynchronously () का उपयोग करना है:

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

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


1
लेकिन जब हम async कोड को अपनी आवश्यकता के अनुसार लौटाते हैं तो हम इस विधि का उपयोग कैसे कर सकते हैं?
S.Serpooshan

16

मैंने इसे कुछ समय का सामना किया है, ज्यादातर इकाई परीक्षण में या विंडोज़ सेवा विकास में। वर्तमान में मैं हमेशा इस सुविधा का उपयोग करता हूं:

        var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
        {
            Trace.WriteLine("Task runSync Start");
            await TaskEx.Delay(2000); // Simulates a method that returns a task and
                                      // inside it is possible that there
                                      // async keywords or anothers tasks
            Trace.WriteLine("Task runSync Completed");
        })).Unwrap();
        Trace.WriteLine("Before runSync Wait");
        runSync.Wait();
        Trace.WriteLine("After runSync Waited");

यह सरल, आसान है और मुझे कोई समस्या नहीं थी।


यह केवल वही है जो मेरे लिए गतिरोध नहीं था।
आंद्रेफेइजो

15

मुझे यह कोड Microsoft.AspNet.Identity.Core घटक में मिला, और यह काम करता है।

private static readonly TaskFactory _myTaskFactory = new 
     TaskFactory(CancellationToken.None, TaskCreationOptions.None, 
     TaskContinuationOptions.None, TaskScheduler.Default);

// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
    CultureInfo cultureUi = CultureInfo.CurrentUICulture;
    CultureInfo culture = CultureInfo.CurrentCulture;
    return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUi;
        return func();
    }).Unwrap<TResult>().GetAwaiter().GetResult();
}


13

बस थोड़ा सा ध्यान दें - यह दृष्टिकोण:

Task<Customer> task = GetCustomers();
task.Wait()

WinRT के लिए काम करता है।

मुझे समझाने दो:

private void TestMethod()
{
    Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
    task.Wait(); // wait executing the method
    var customer = task.Result; // get's result.
    Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
    public Customer()
    {
        new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
    }
    public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
    return Task.Run(() => new Customer
    {
        Name = "MyName"
    });
}

इसके अलावा यह दृष्टिकोण केवल विंडोज स्टोर समाधान के लिए काम करता है!

नोट: यदि आप अपनी विधि को अन्य async विधि के अंदर कहते हैं तो यह तरीका सुरक्षित नहीं है (@ टिप्पणियों के अनुसार)


मैंने इस समाधान को समझाया, EDIT अनुभाग की जाँच करें।
RredCat

2
यह बहुत आसानी से गतिरोध में परिणाम कर सकता है जब अतुल्यकालिक स्थितियों में कहा जाता है।
सेवाकाल

@ सरवाइव मतलब। तो जैसा कि मैं वेट (टाइमऑट) का उपयोग करके सही हो जाता हूं, ठीक है?
र्रेडकट

1
तब आपको इस बात की चिंता करने की ज़रूरत है कि जब ऑपरेशन वास्तव में नहीं किया जाता है, तो समय पर पहुंच गया, जो बहुत बुरा है, और यह भी कि जब तक यह गतिरोध (और उस स्थिति में आप अभी भी जारी है , समय समाप्त होने तक प्रतीक्षा में बिताया गया समय जब यह नहीं किया जाता है)। तो नहीं, यह समस्या को ठीक नहीं करता है।
सेवाकाल

@ सरवी को लगता है कि मुझे CancellationTokenअपने समाधान के लिए लागू करना होगा।
र्रेडकैट

10

आपके कोड में, आपका पहला कार्य निष्पादित करने के लिए प्रतीक्षा करता है लेकिन आपने इसे शुरू नहीं किया है इसलिए यह अनिश्चित काल तक प्रतीक्षा करता है। इसे इस्तेमाल करे:

Task<Customer> task = GetCustomers();
task.RunSynchronously();

संपादित करें:

आप कहते हैं कि आपको एक अपवाद मिलता है। स्टैक ट्रेस सहित अधिक जानकारी पोस्ट करें।
मोनो में निम्नलिखित परीक्षण मामले शामिल हैं:

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

जांचें कि यह आपके लिए काम करता है। यदि यह नहीं है, हालांकि बहुत संभावना नहीं है, तो आपके पास Async CTP का कुछ विषम निर्माण हो सकता है। यदि यह काम करता है, तो आप जांचना चाह सकते हैं कि कंपाइलर वास्तव में क्या उत्पन्न करता है और Taskइस नमूने से कैसे अलग है।

# 2 संपादित करें:

मैंने परावर्तक के साथ जाँच की कि आपके द्वारा वर्णित अपवाद तब होता है जब m_actionवह होता है null। यह थोड़े अजीब है, लेकिन मैं Async CTP पर कोई विशेषज्ञ नहीं हूँ। जैसा कि मैंने कहा, आपको अपने कोड को अपघटित करना चाहिए और देखना चाहिए कि वास्तव Taskमें किसी भी तरह से कैसे त्वरित किया जा रहा m_actionहै null


पुनश्च सामयिक चढ़ाव के साथ सौदा क्या है? विस्तृत करने के लिए परवाह?


मैंने अपना प्रश्न उस कोड को बनाने के लिए समायोजित किया जिसे मैंने थोड़ा स्पष्ट करने का प्रयास किया था। RunSynchronously की एक त्रुटि देता है RunSynchronously may not be called on a task unbound to a delegate। चीनी के लिए सभी परिणामों के बाद से Google कोई मदद नहीं कर रहा है ...
राहेल

मुझे लगता है कि अंतर यह है कि मैं टास्क नहीं बनाता और फिर इसे चलाने की कोशिश करता हूं। इसके बजाय, जब awaitकीवर्ड का उपयोग किया जाता है तो कार्य को एस्किंस विधि द्वारा बनाया जाता है। मेरे पहले की टिप्पणी में पोस्ट किया गया अपवाद मुझे मिलने वाला अपवाद है, हालांकि यह उन कुछ में से एक है जो मैं Google नहीं कर सकता और इसके लिए एक कारण या संकल्प ढूंढ सकता हूं।
राहेल

1
asyncऔर asyncखोजशब्द सिंटेक्स चीनी से अधिक कुछ नहीं हैं। कंपाइलर कोड बनाता Task<Customer>है GetCustomers()ताकि मैं पहले कहां देखूं। अपवाद के लिए, आपने केवल अपवाद संदेश पोस्ट किया है, जो अपवाद प्रकार और स्टैक ट्रेस के बिना बेकार है। ToString()प्रश्न में अपवाद की विधि और पोस्ट आउटपुट को कॉल करें ।
दान अब्रामोव

@ ओगैरॉन: मैंने अपने मूल प्रश्न में अपवाद विवरण और स्टैक ट्रेस पोस्ट किए।
राहेल

2
@gaearon मुझे लगता है कि आप डाउनवोट हो गए थे क्योंकि आपकी पोस्ट सवाल पर लागू नहीं होती है। चर्चा async-wait विधियों के बारे में है, न कि साधारण टास्क-रिटर्निंग विधियों के बारे में। इसके अलावा, मेरी राय में, async-wait तंत्र एक वाक्यविन्यास चीनी है, लेकिन इतना तुच्छ नहीं - निरंतरता, संदर्भ कैप्चरिंग, स्थानीय संदर्भ फिर से शुरू करना, स्थानीय अपवादों को संभालना, और बहुत कुछ है। उसके बाद, आपको Async विधि के परिणाम पर RunSynchronously पद्धति का आह्वान नहीं करना चाहिए , क्योंकि परिभाषा द्वारा अतुल्यकालिक विधि को टास्क को वापस करना चाहिए जो वर्तमान में कम से कम अनुसूचित है, और एक से अधिक बार चालू स्थिति में है।
sgnsajgon

9

.Net 4.6 में परीक्षण किया गया। यह गतिरोध से भी बच सकता है।

Async विधि के लिए Task

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();

Async विधि के लिए Task<T>

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;

संपादित करें :

यदि कॉलर थ्रेड पूल थ्रेड में चल रहा है (या कॉल करने वाला भी किसी कार्य में है), तो यह अभी भी कुछ स्थिति में गतिरोध का कारण हो सकता है।


1
लगभग 8 वर्षों के बाद मेरा उत्तरदाता :) दूसरा उदाहरण - मुख्य रूप से उपयोग किए जाने वाले सभी निर्धारित संदर्भों में एक गतिरोध उत्पन्न करेगा (कंसोल ऐप / .NET कोर / डेस्कटॉप ऐप / ...)। यहाँ आप और अधिक अवलोकन कर रहे हैं कि मैं अब किस बारे में बात कर रहा हूँ: medium.com/rubrikkgroup/…
W92

Resultनौकरी के लिए एकदम सही है अगर आप एक तुल्यकालिक कॉल चाहते हैं, और बिल्कुल खतरनाक अन्यथा। नाम में कुछ भी नहीं है Resultया इस Resultबात का संकेत नहीं है कि यह एक अवरुद्ध कॉल है। इसका वास्तव में नाम बदला जाना चाहिए।
राशिमान

5

कोड स्निप के नीचे का उपयोग करें

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));


3

यह उत्तर किसी के लिए बनाया गया है जो .NET 4.5 के लिए WPF का उपयोग कर रहा है।

यदि आप Task.Run()GUI थ्रेड पर निष्पादित करने का प्रयास करते हैं , तो task.Wait()अनिश्चित काल तक लटका रहेगा, यदि आपके पास asyncफ़ंक्शन फ़ंक्शन में कीवर्ड नहीं है ।

यह विस्तार विधि यह देखने के लिए जाँच करके समस्या हल करती है कि क्या हम GUI थ्रेड पर हैं, और यदि ऐसा है, तो WPF डिस्पैचर थ्रेड पर कार्य चला रहा है।

यह वर्ग async / प्रतीक्षा दुनिया और गैर- async / प्रतीक्षा दुनिया के बीच गोंद के रूप में कार्य कर सकता है, ऐसी स्थितियों में यह अपरिहार्य है, जैसे कि MVVM गुण या अन्य API पर निर्भरताएं जो async / प्रतीक्षा का उपयोग नहीं करते हैं।

/// <summary>
///     Intent: runs an async/await task synchronously. Designed for use with WPF.
///     Normally, under WPF, if task.Wait() is executed on the GUI thread without async
///     in the function signature, it will hang with a threading deadlock, this class 
///     solves that problem.
/// </summary>
public static class TaskHelper
{
    public static void MyRunTaskSynchronously(this Task task)
    {
        if (MyIfWpfDispatcherThread)
        {
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E99213. Task did not run to completion.");
            }
        }
        else
        {
            task.Wait();
            if (task.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E33213. Task did not run to completion.");
            }
        }
    }

    public static T MyRunTaskSynchronously<T>(this Task<T> task)
    {       
        if (MyIfWpfDispatcherThread)
        {
            T res = default(T);
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E89213. Task did not run to completion.");
            }
            return res;
        }
        else
        {
            T res = default(T);
            var result = Task.Run(async () => res = await task);
            result.Wait();
            if (result.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E12823. Task did not run to completion.");
            }
            return res;
        }
    }

    /// <summary>
    ///     If the task is running on the WPF dispatcher thread.
    /// </summary>
    public static bool MyIfWpfDispatcherThread
    {
        get
        {
            return Application.Current.Dispatcher.CheckAccess();
        }
    }
}

3

बस कॉलिंग .Result;या .Wait()गतिरोध के लिए एक जोखिम है जैसा कि कई ने टिप्पणियों में कहा है। चूँकि हम में से अधिकांश लोग ओनलिनर्स को पसंद करते हैं, इसलिए आप इनका उपयोग कर सकते हैं.Net 4.5<

एक async विधि के माध्यम से एक मूल्य प्राप्त करना:

var result = Task.Run(() => asyncGetValue()).Result;

एसिंक्रोनस तरीके से कॉल करना

Task.Run(() => asyncMethod()).Wait();

के उपयोग के कारण कोई गतिरोध के मुद्दे नहीं होंगे Task.Run

स्रोत:

https://stackoverflow.com/a/32429753/3850405


1

मुझे लगता है कि निम्न सहायक विधि भी समस्या को हल कर सकती है।

private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
    {
        TResult result = default(TResult);
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try
            {
                result = await func();
            }
            catch (Exception exc)
            {
                mErrorLogger.LogError(exc.ToString());
            }
            finally
            {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

निम्नलिखित तरीके से इस्तेमाल किया जा सकता है:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);

1
कृपया मतदान को स्पष्ट करें
नॉट्टेलीया

2
... मुझे अभी भी उत्सुकता से दिलचस्पी है कि इस जवाब को वोट क्यों दिया गया?
नॉटटेलीला

यह एक सही "तुल्यकालिक" नहीं है। आप दो धागे बना सकते हैं और दूसरे के पहले परिणामों में प्रतीक्षा कर सकते हैं।
tmt 14

और सभी चीजें एक तरफ, यह एक बहुत बुरा विचार है।
डैन पेंट्री

1
मैंने बस लगभग समान कोड (लाइन बाय लाइन) लिखा है, लेकिन ऑटो रीसेट घटना के बजाय सेमीफोरस्मिल का उपयोग कर रहा हूं। काश मैंने इसे जल्दी ही देख लिया होता। मैं गतिरोध को रोकने के लिए इस दृष्टिकोण को ढूंढता हूं और आपके एसिंक्स कोड को उसी तरह से चालू रखता है जैसा कि सच्चे अतुल्यकालिक परिदृश्यों में होता है। वास्तव में यकीन नहीं है कि यह एक बुरा विचार क्यों है। ऊपर देखे गए अन्य दृष्टिकोणों की तुलना में बहुत अधिक क्लीनर लगता है।
tmrog

0

यह मेरे लिए काम करता है

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public static class AsyncHelper
    {
        private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

        public static void RunSync(Func<Task> func)
        {
            _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }

        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }
    }

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}

-1

मैंने पाया है कि स्पिनवाट इसके लिए बहुत अच्छा काम करता है।

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

उपर्युक्त दृष्टिकोण का उपयोग करने की आवश्यकता नहीं है .Result या .Wait ()। यह आपको एक टाइमआउट भी निर्दिष्ट करने देता है ताकि कार्य पूरा न होने की स्थिति में आप हमेशा के लिए अटक न जाएं।


1
डाउनवोट बताता है कि किसी को यह तरीका पसंद नहीं है। क्या कोई ऐसा व्यक्ति है जो इसके बारे में टिप्पणी कर सकता है?
ग्रैक्स 32

Downvoter की अनुपस्थिति में यह कहते हुए कि क्यों downvote दिया गया था, क्या कोई इसे बढ़ा सकता है? :-)
कर्टिस

1
यह मतदान (कताई) है, प्रतिनिधि प्रति सेकंड 1000 बार तक पूल से धागा ले जाएगा। यह कार्य समाप्त होने के तुरंत बाद नियंत्रण वापस नहीं कर सकता ( 10 + एमएस त्रुटि तक)। यदि समय समाप्त करके कार्य जारी रहेगा, जो समय-समय पर व्यावहारिक रूप से बेकार हो जाता है।
सिनैट्र

वास्तव में, मैं अपने कोड में सभी जगह इसका उपयोग कर रहा हूं और जब शर्त पूरी हो जाती है, तो स्पिनवाइटस्पिन यूटिल () का उपयोग बंद हो जाता है। इसलिए जो भी पहले आता है, 'शर्त पूरी होती है' या टाइमआउट, कार्य से बाहर निकल जाता है। यह चलता नहीं रहता है।
कर्टिस

-3

Wp8 पर:

इसे लपेटो:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

इसे कहते हैं:

GetCustomersSynchronously();

3
नहीं, यह काम नहीं करेगा, क्योंकि कार्य निर्माणकर्ता से प्रतिनिधि का इंतजार नहीं करता है (एक प्रतिनिधि और एक कार्य नहीं ..)
रिको सोटर

-4
    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }

-5

या आप बस साथ जा सकते हैं:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

यह सुनिश्चित करने के लिए कि आप संदर्भ विस्तार विधानसभा का संकलन करें:

System.Net.Http.Formatting

-9

निम्नलिखित कोड का प्रयास करें यह मेरे लिए काम करता है:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.