c# - सी#में सिंक्रोनस विधि से एसिंक्रोनस विधि कैसे कॉल करें?




async-await (8)

async मुख्य अब सी # 7.2 का हिस्सा है और परियोजनाओं में उन्नत बिल्ड सेटिंग्स में सक्षम किया जा सकता है।

सी # <7.2 के लिए, सही तरीका है:

static void Main(string[] args)
{
   MainAsync().GetAwaiter().GetResult();
}


static async Task MainAsync()
{
   /*await stuff here*/
}

मेरे पास एक public async void Foo() विधि है जिसे मैं सिंक्रोनस विधि से कॉल करना चाहता हूं। अब तक मैंने एमएसडीएन दस्तावेज से देखा है एसिंक तरीकों के माध्यम से async विधियों को बुला रहा है, लेकिन मेरा पूरा कार्यक्रम async विधियों के साथ बनाया नहीं गया है।

क्या यह भी संभव है?

इन विधियों को एसिंक्रोनस विधि से कॉल करने का एक उदाहरण यहां दिया गया है: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

अब मैं सिंक विधियों से इन async विधियों को कॉल करने के लिए देख रहा हूँ।


असीमित प्रोग्रामिंग कोड बेस के माध्यम से "बढ़ती" है। इसकी तुलना एक ज़ोंबी वायरस से की गई है । सबसे अच्छा समाधान यह बढ़ने की अनुमति देना है, लेकिन कभी-कभी यह संभव नहीं है।

मैंने आंशिक रूप से एसिंक्रोनस कोड बेस से निपटने के लिए मेरे Nito.AsyncEx लाइब्रेरी में कुछ प्रकार लिखे हैं। हालांकि, कोई समाधान नहीं है जो हर स्थिति में काम करता है।

समाधान ए

यदि आपके पास एक साधारण एसिंक्रोनस विधि है जिसे इसके संदर्भ में वापस सिंक्रनाइज़ करने की आवश्यकता नहीं है, तो आप कार्य का उपयोग कर सकते हैं। Task.WaitAndUnwrapException :

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

आप Task.Wait या Task.Result का उपयोग नहीं करना चाहते हैं क्योंकि वे AggregateException में अपवाद लपेटते हैं।

यह समाधान केवल तभी उचित है जब MyAsyncMethod इसके संदर्भ में वापस सिंक्रनाइज़ नहीं करता है। दूसरे शब्दों में, MyAsyncMethod में प्रत्येक await को ConfigureAwait(false) साथ समाप्त होना चाहिए। इसका अर्थ है कि यह किसी भी यूआई तत्वों को अपडेट नहीं कर सकता है या एएसपी.NET अनुरोध संदर्भ तक नहीं पहुंच सकता है।

समाधान बी

यदि MyAsyncMethod को इसके संदर्भ में वापस सिंक्रनाइज़ करने की आवश्यकता है, तो आप एक नेस्टेड संदर्भ प्रदान करने के लिए AsyncContext.RunTask का उपयोग करने में सक्षम हो सकते हैं:

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

* अद्यतन 4/14/2014: पुस्तकालय के हाल के संस्करणों में एपीआई निम्नानुसार है:

var result = AsyncContext.Run(MyAsyncMethod);

(कार्य का उपयोग करना ठीक है। इस उदाहरण में परिणाम क्योंकि RunTask Task अपवादों का प्रचार करेगा)।

कारण आपको AsyncContext.RunTask आवश्यकता हो सकती है AsyncContext.RunTask Winforms / WPF / SL / ASP.NET पर होने वाली सूक्ष्म डेडलॉक संभावना के कारण है:

  1. एक सिंक्रोनस विधि एक Task प्राप्त करने, एक async विधि कहते हैं।
  2. तुल्यकालिक विधि Task पर अवरुद्ध प्रतीक्षा करता है।
  3. async विधि ConfigureAwait बिना await का उपयोग await
  4. Task इस स्थिति में पूरा नहीं हो सकता है क्योंकि यह केवल तब पूरा होता है जब async विधि समाप्त हो जाती है; async विधि पूर्ण नहीं हो सकती है क्योंकि यह सिंक्रनाइज़ेशन कॉन्टेक्स्ट को अपनी निरंतरता निर्धारित करने का प्रयास कर रहा है, और WinForms / WPF / SL / ASP.NET निरंतरता को चलाने की अनुमति नहीं देगा क्योंकि सिंक्रोनस विधि पहले से ही उस संदर्भ में चल रही है।

यह एक कारण है कि जितना संभव हो सके प्रत्येक async विधि में ConfigureAwait(false) का उपयोग ConfigureAwait(false) एक अच्छा विचार है।

समाधान सी

AsyncContext.RunTask प्रत्येक परिदृश्य में काम नहीं करेगा। उदाहरण के लिए, यदि async विधि कुछ ऐसा इंतजार कर रही है जिसके लिए यूआई इवेंट पूरा करने की आवश्यकता है, तो आप नेस्टेड संदर्भ के साथ भी डेडलॉक करेंगे। उस स्थिति में, आप थ्रेड पूल पर async विधि शुरू कर सकते हैं:

var task = TaskEx.RunEx(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

हालांकि, इस समाधान के लिए एक MyAsyncMethod आवश्यकता है जो थ्रेड पूल संदर्भ में काम करेगा। इसलिए यह यूआई तत्वों को अपडेट नहीं कर सकता है या एएसपी.NET अनुरोध संदर्भ तक नहीं पहुंच सकता है। और उस स्थिति में, आप इसके await वक्तव्य में ConfigureAwait(false) भी जोड़ सकते हैं, और समाधान ए का उपयोग कर सकते हैं।


आप सिंक्रोनस कोड से किसी भी एसिंक्रोनस विधि को कॉल कर सकते हैं, यानी, जब तक आपको उन पर await करने की आवश्यकता न हो, उस स्थिति में उन्हें async भी चिह्नित किया जाना चाहिए।

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

मैं वास्तव में आपकी विधि async नहीं बना सकता और आप सिंक्रोनस विधि को लॉक नहीं करना चाहते हैं, तो आपको कार्य पर जारी With विधि के पैरामीटर के रूप में इसे पास करके कॉलबैक विधि का उपयोग करना होगा।


उन विंडोज़ एसिंक विधियों में एक निफ्टी छोटी विधि है जिसे AsTask () कहा जाता है। आप इसे एक कार्य के रूप में विधि को वापस करने के लिए इसका उपयोग कर सकते हैं ताकि आप इसे प्रतीक्षा () पर मैन्युअल रूप से कॉल कर सकें।

उदाहरण के लिए, विंडोज फोन 8 सिल्वरलाइट एप्लिकेशन पर, आप निम्न कार्य कर सकते हैं:

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

उम्मीद है की यह मदद करेगा!


माइक्रोसॉफ्ट ने Async को सिंक के रूप में चलाने के लिए एक AsyncHelper (आंतरिक) कक्षा बनाई। स्रोत इस तरह दिखता है:

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

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

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

Microsoft.AspNet.Identity बेस क्लास में केवल Async विधियां हैं और उन्हें सिंक के रूप में कॉल करने के लिए वहां विस्तार विधियों वाले वर्ग हैं (उदाहरण के उपयोग):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

मैं 100% निश्चित नहीं हूं, लेकिन मेरा मानना ​​है कि इस ब्लॉग में वर्णित तकनीक को कई परिस्थितियों में काम करना चाहिए:

इस प्रकार आप कार्य का उपयोग कर सकते हैं। task.GetAwaiter().GetResult() यदि आप सीधे इस प्रचार तर्क का आह्वान करना चाहते हैं।


   //Example from non UI thread -    
   private void SaveAssetAsDraft()
    {
        SaveAssetDataAsDraft();
    }
    private async Task<bool> SaveAssetDataAsDraft()
    {
       var id = await _assetServiceManager.SavePendingAssetAsDraft();
       return true;   
    }
   //UI Thread - 
   var result = Task.Run(() => SaveAssetDataAsDraft().Result).Result;

public async Task<string> StartMyTask()
{
    await Foo()
    // code to execute once foo is done
}

static void Main()
{
     var myTask = StartMyTask(); // call your method which will return control once it hits await
     // now you can continue executing code here
     string result = myTask.Result; // wait for the task to complete to continue
     // use result

}

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





async-await