C # में सिंक्रोनस विधि से अतुल्यकालिक विधि को कैसे कॉल करें?


861

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

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

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

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


2
मैं इसमें भी भाग गया। एक रोलपॉइडर को ओवरराइड करने से आप GetRolesForUser मेथड के मेथड सिग्नेचर को नहीं बदल सकते हैं, ताकि आप इस विधि को एसिंक्रोनस न बना सकें और इसलिए एसिटिक रूप से आपी को कॉल करने के लिए वेट का उपयोग नहीं कर सकते। मेरा अस्थायी समाधान मेरे जेनेरिक HttpClient वर्ग के लिए तुल्यकालिक तरीकों को जोड़ना था, लेकिन यह जानना चाहते हैं कि क्या यह संभव है (और क्या प्रभाव हो सकता है)।
टिमोथी ली रसेल

1
क्योंकि आपकी async void Foo()विधि वापस नहीं आती है, Taskइसका मतलब है कि कॉल करने वाला यह नहीं जानता कि यह कब पूरा होता है, इसके Taskबजाय वापस लौटना चाहिए।
दाई

1
एक यूआई थ्रेड पर ऐसा करने के लिए संबंधित क्यू / एक को जोड़ना ।
noseratio

जवाबों:


710

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

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

समाधान ए

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

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

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

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

समाधान बी

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

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

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

var result = AsyncContext.Run(MyAsyncMethod);

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

इसके AsyncContext.RunTaskबजाय आपको इसकी आवश्यकता हो सकती Task.WaitAndUnwrapExceptionहै क्योंकि WinForms / WPF / SL / ASP.NET पर होने वाले एक सूक्ष्म गतिरोध की संभावना के कारण है:

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

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

समाधान सी

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

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

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

अपडेट, 2019-05-01: वर्तमान "सबसे कम-सबसे खराब प्रथाएं" यहां एक एमएसडीएन लेख में हैं


9
समाधान ए ऐसा लगता है जैसे मैं चाहता हूं, लेकिन यह कार्य जैसा दिखता है। aitAndUnwrapException () ने इसे .net 4.5 RC में नहीं डाला; यह केवल कार्य है। किसी भी विचार कैसे नए संस्करण के साथ ऐसा करने के लिए? या यह एक कस्टम एक्सटेंशन विधि है जिसे आपने लिखा है?
घातक जूल

3
WaitAndUnwrapExceptionमेरे AsyncEx पुस्तकालय से मेरा अपना तरीका है । आधिकारिक .NET .NETs सिंक और एसिंक्स कोड (और सामान्य रूप से, आपको ऐसा नहीं करना चाहिए!) को मिलाने के लिए बहुत मदद नहीं करते हैं। मैं 4.5 पर चलने के लिए AsyncEx को अपडेट करने से पहले .NET 4.5 RTW और एक नए गैर-XP लैपटॉप की प्रतीक्षा कर रहा हूं (मैं वर्तमान में 4.5 के लिए विकसित नहीं कर सकता क्योंकि मैं कुछ और हफ्तों के लिए XP पर अटक गया हूं)।
स्टीफन क्लीयर

12
AsyncContextअब एक Runविधि है जो लंबोदर अभिव्यक्ति लेती है, इसलिए आपको उपयोग करना चाहिएvar result = AsyncContext.Run(() => MyAsyncMethod());
स्टीफन क्लीयर

1
मुझे आपकी लाइब्रेरी नुगेट से मिल गई, लेकिन यह वास्तव में एक RunTaskविधि नहीं है। निकटतम चीज जो मुझे मिल सकती थी Run, वह थी , लेकिन उसके पास कोई Resultसंपत्ति नहीं थी।
असद सईदुद्दीन

3
@ एडैड: हां, 2 साल से अधिक समय बाद एपीआई बदल गया है। अब आप बस कह सकते हैंvar result = AsyncContext.Run(MyAsyncMethod);
स्टीफन क्लीरी

313

एक ऐसा समाधान जोड़ना जो अंत में मेरी समस्या को हल करे, उम्मीद है कि किसी के समय को बचाता है।

सबसे पहले स्टीफन क्लीरी के कुछ लेख पढ़ें :

"असॉक कोड पर ब्लॉक न करें" में "दो सर्वोत्तम प्रथाओं" से, पहला मेरे लिए काम नहीं करता था और दूसरा लागू नहीं था (मूल रूप से अगर मैं उपयोग कर सकता awaitहूं, तो मैं!)।

तो यहाँ मेरा वर्कअराउंड है: कॉल को अंदर लपेटें Task.Run<>(async () => await FunctionAsync());और उम्मीद है कि अब कोई गतिरोध न हो ।

यहाँ मेरा कोड है:

public class LogReader
{
    ILogger _logger;

    public LogReader(ILogger logger)
    {
        _logger = logger;
    }

    public LogEntity GetLog()
    {
        Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
        return task.Result;
    }

    public async Task<LogEntity> GetLogAsync()
    {
        var result = await _logger.GetAsync();
        // more code here...
        return result as LogEntity;
    }
}

5
दो साल, मैं यह जानने के लिए उत्सुक हूं कि यह समाधान कैसे हो रहा है। कोई खबर? क्या इस दृष्टिकोण में सूक्ष्मता है जो न्यूबाय पर खो गया है?
डैन एस्परज़ा

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

29
@ChrisPratt - आप सही हो सकते हैं, क्योंकि Task.Run()एक async कोड में सबसे अच्छा अभ्यास नहीं है। लेकिन, फिर से, मूल प्रश्न का उत्तर क्या है? सिंक्रोनस विधि को कभी भी सिंक्रोनाइज़ नहीं करें? हम चाहते हैं, लेकिन एक वास्तविक दुनिया में, कभी-कभी हमें करना पड़ता है।
Tohid

1
@ यदि आप स्टीफन क्लीरी के पुस्तकालय की कोशिश कर सकते हैं। मैंने देखा है कि लोग इसे मान लेते हैं और Parallel.ForEachदुरुपयोग का 'वास्तविक दुनिया' में कोई प्रभाव नहीं पड़ेगा और अंततः इसने सर्वरों को नीचे ले लिया। यह कोड कंसोल ऐप्स के लिए ठीक है, लेकिन जैसा @ChrisPratt कहता है, उसे वेब ऐप्स में उपयोग नहीं किया जाना चाहिए। यह "अब" काम कर सकता है, लेकिन स्केलेबल नहीं है।
18

1
मैं नए अकाउंट बनाने की शुरुआत करने के लिए तैयार हूं, इसलिए सवालों के जवाब देने के लिए पर्याप्त अंक प्राप्त करने के लिए इस एक को उभारने के लिए ....
जियानिस परस्केवोपुलस

206

Microsoft ने 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));
}

कोड की लाइसेंस शर्तों के बारे में चिंतित लोगों के लिए, यहां बहुत समान कोड (बस थ्रेड पर संस्कृति के लिए समर्थन जोड़ता है) की एक कड़ी है, जिसमें यह इंगित करने के लिए टिप्पणियां हैं कि यह Microsoft द्वारा एमआईटी लाइसेंस प्राप्त है। https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


2
मेरी async विधियाँ अन्य async विधियों की प्रतीक्षा करती हैं। मैं अपनी किसी भी awaitकॉल को नहीं सजाता ConfigureAwait(false)। मैंने Global.asax में AsyncHelper.RunSyncफ़ंक्शन से एक async फ़ंक्शन को कॉल करने का उपयोग करने की कोशिश की Application_Start()और यह काम करने लगता है। क्या इसका मतलब यह है कि AsyncHelper.RunSyncमज़बूती से "कॉलर के संदर्भ में मार्शल वापस" गतिरोध मुद्दा नहीं है जो मैंने इस पोस्टिंग में कहीं और पढ़ा है?
Bob.at.Indigo.Health

1
@ Bob.at.SBS क्या आप कोड करता है पर निर्भर करता है। यह उतना सरल नहीं है, जितना कि मैं इस कोड का उपयोग करता हूं मैं सुरक्षित हूं । यह बहुत ही न्यूनतम और अर्ध-सुरक्षित तरीका है जो कि एसिंक्स कमांड को सिंक्रोनाइज़ करने के लिए चलाया जाता है, इसे आसानी से अनुचित तरीके से गतिरोध पैदा करने के लिए इस्तेमाल किया जा सकता है।
एरिक फिलिप्स

1
धन्यवाद। 2 फॉलो-अप प्रश्न: 1) क्या आप किसी ऐसी चीज का उदाहरण दे सकते हैं जो एस्किंस विधि से बचना चाहती है जिससे गतिरोध पैदा हो, और 2) इस संदर्भ में गतिरोध अक्सर समय-निर्भर हैं? यदि यह व्यवहार में काम करता है, तो क्या मुझे अभी भी अपने कोड में एक समय-निर्भर गतिरोध की आशंका है?
Bob.at.Indigo.Health

@ Bob.at.SBS मैं शीर्ष दाईं ओर स्थित प्रश्न बटन का उपयोग करके प्रश्न पूछने की सलाह दूंगा । आप इस प्रश्न का लिंक शामिल कर सकते हैं या संदर्भ के रूप में अपने प्रश्न का उत्तर दे सकते हैं।
एरिक फिलिप्स

1
@ Bob.at ... Erik द्वारा प्रदान किया गया कोड Asp के तहत एकदम सही काम करता है। शुद्ध mvc5 और EF6, लेकिन नहीं है जब मैं अन्य समाधान (ConfigureAwait (गलत) .GetAwaiter () GetResult () या .result।) के किसी भी करने की कोशिश की जो रुक जाता है पूरी तरह से मेरी वेब अनुप्रयोग
LeonardoX

150

Async Main अब C # 7.2 का हिस्सा है और इसे उन्नत बिल्ड सेटिंग्स में सक्षम किया जा सकता है।

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

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


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

आप इसे बहुत सारे Microsoft दस्तावेज़ों में उपयोग करते देखेंगे, उदाहरण के लिए: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use- विषय-सदस्यता


11
मुझे अंदाजा नहीं है कि किसी ने इसे क्यों वोट दिया। यह मेरे लिए बहुत अच्छा काम किया। इस फिक्स के बिना, मुझे ASYCH EVERYWHERE का प्रचार करना होगा।
कैदी जीरो

11
इससे बेहतर क्यों है MainAsync().Wait()?
क्रश

8
मैं सहमत हूँ। आपको इस सब के बजाय सिर्फ MainAsync () की आवश्यकता है। रुको ()।
हज्जत

8
@ क्रश मैं वर्णन कर रहा था कि यह कुछ गतिरोधों से कैसे बचा जा सकता है। कुछ स्थितियों में कॉलिंग .Wait () से UI या asp.net थ्रेड के कारण गतिरोध होता है। async गतिरोध
डेविड

6
@ClintB: आपको ASP.NET कोर में बिल्कुल ऐसा नहीं करना चाहिए। वेब एप्लिकेशन विशेष रूप से थ्रेड-भूखे होने के लिए कमजोर होते हैं, और हर बार जब आप ऐसा करते हैं, तो आप पूल से एक थ्रेड खींच रहे हैं जो अन्यथा अनुरोध करने के लिए उपयोग किया जाएगा। यह डेस्कटॉप / मोबाइल अनुप्रयोगों के लिए कम समस्याग्रस्त है क्योंकि वे पारंपरिक रूप से एकल-उपयोगकर्ता हैं।
क्रिस प्रैट

52
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

}

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


15
Waitअपवादों को लपेटता है और गतिरोध की संभावना है।
स्टीफन क्लीयर

मैंने सोचा कि यदि आप बिना उपयोग किए एक async विधि कहते हैं await, तो इसे सिंक्रोनाइज़ किया जाएगा। कम से कम जो मेरे लिए (बिना बुलाए myTask.Wait) काम करता है । वास्तव में, मुझे एक अपवाद मिला जब मैंने कॉल करने की कोशिश की myTask.RunSynchronously()क्योंकि यह पहले ही निष्पादित हो चुका था!
विस्मय

2
मुझे यह उत्तर पसंद है। संपादन, छोटी और सुरुचिपूर्ण के लिए अच्छी टिप्पणियाँ। योगदान के लिए धन्यवाद! मैं अभी भी
संक्षिप्त रूप से

2
क्या यह जवाब आज भी काम करना चाहिए? मैंने अभी इसे एमवीसी रेजर प्रोजेक्ट में आज़माया है और ऐप केवल एक्सेस करने पर लटका हुआ है .Result
कोडिंग

7
@TrueBlueAussie यह सिंक्रनाइज़ेशन संदर्भ गतिरोध है। आपके async कोड को सिंक्रनाइज़ेशन संदर्भ में वापस भेज दिया गया है, लेकिन उस Resultसमय कॉल द्वारा अवरुद्ध किया जा रहा है , इसलिए यह कभी भी वहां नहीं जाता है। और Resultकभी समाप्त नहीं होता है, क्योंकि यह किसी ऐसे व्यक्ति की प्रतीक्षा कर रहा है जो Resultअंत तक इंतजार कर रहा है , मूल रूप से: डी
लुअन

40

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

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


6
स्टीफन क्ली के उत्तर में समाधान ए इस पद्धति का उपयोग करता है। WaitAndUnwrapException स्रोत देखें ।
17

क्या आपको उपयोग करने की आवश्यकता है GetResult () यदि आप जिस फ़ंक्शन को कॉल कर रहे हैं वह शून्य या कार्य है? मेरा मतलब है कि यदि आप किसी परिणाम को वापस नहीं लेना चाहते हैं
बैटमेकी

हां, अन्यथा यह कार्य पूरा होने तक अवरुद्ध नहीं होगा। वैकल्पिक रूप से GetAwaiter () कॉल करने के बजाय। GetResult () आप कॉल कर सकते हैं। Wait ()
NStuke

1
यह "कई परिस्थितियों" का हिस्सा है। यह समग्र थ्रेडिंग मॉडल पर निर्भर करता है और यह निर्धारित करने के लिए अन्य थ्रेड्स क्या कर रहे हैं कि क्या गतिरोध का खतरा है या नहीं।
NStuke

24

हालाँकि, एक अच्छा समाधान है जो हर स्थिति (लगभग: टिप्पणियों को देखें) में काम करता है: एक एड-हॉक संदेश पंप (सिंक्रोनाइज़ेशनकोटेक्स्ट)।

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

तदर्थ संदेश पंप सहायक का कोड:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Threading
{
    /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
    public static class AsyncPump
    {
        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Action asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(true);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function
                syncCtx.OperationStarted();
                asyncMethod();
                syncCtx.OperationCompleted();

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Func<Task> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static T Run<T>(Func<Task<T>> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                return t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
        private sealed class SingleThreadSynchronizationContext : SynchronizationContext
        {
            /// <summary>The queue of work items.</summary>
            private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
                new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
            /// <summary>The processing thread.</summary>
            private readonly Thread m_thread = Thread.CurrentThread;
            /// <summary>The number of outstanding operations.</summary>
            private int m_operationCount = 0;
            /// <summary>Whether to track operations m_operationCount.</summary>
            private readonly bool m_trackOperations;

            /// <summary>Initializes the context.</summary>
            /// <param name="trackOperations">Whether to track operation count.</param>
            internal SingleThreadSynchronizationContext(bool trackOperations)
            {
                m_trackOperations = trackOperations;
            }

            /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
            /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
            /// <param name="state">The object passed to the delegate.</param>
            public override void Post(SendOrPostCallback d, object state)
            {
                if (d == null) throw new ArgumentNullException("d");
                m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
            }

            /// <summary>Not supported.</summary>
            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("Synchronously sending is not supported.");
            }

            /// <summary>Runs an loop to process all queued work items.</summary>
            public void RunOnCurrentThread()
            {
                foreach (var workItem in m_queue.GetConsumingEnumerable())
                    workItem.Key(workItem.Value);
            }

            /// <summary>Notifies the context that no more work will arrive.</summary>
            public void Complete() { m_queue.CompleteAdding(); }

            /// <summary>Invoked when an async operation is started.</summary>
            public override void OperationStarted()
            {
                if (m_trackOperations)
                    Interlocked.Increment(ref m_operationCount);
            }

            /// <summary>Invoked when an async operation is completed.</summary>
            public override void OperationCompleted()
            {
                if (m_trackOperations &&
                    Interlocked.Decrement(ref m_operationCount) == 0)
                    Complete();
            }
        }
    }
}

उपयोग:

AsyncPump.Run(() => FooAsync(...));

एस्किम पंप का अधिक विस्तृत विवरण यहां उपलब्ध है


अपवाद संदर्भ और AsyncPump stackoverflow.com/questions/23161693/…
PreguntonCojoneroCabrón

यह एक asp.net परिदृश्य में काम नहीं करता है, क्योंकि आप बेतरतीब ढंग से HttpContext.Current खो सकते हैं।
जोश मूच

12

किसी को भी इस सवाल पर ध्यान देने के लिए ...

यदि आप Microsoft.VisualStudio.Services.WebApiवहाँ एक वर्ग बुलाया में देखो TaskExtensions। उस वर्ग के भीतर आप स्थैतिक विस्तार विधि देखेंगेTask.SyncResult() , जो पूरी तरह से कार्य के रिटर्न तक केवल थ्रेड को ब्लॉक करती है।

आंतरिक रूप से यह task.GetAwaiter().GetResult()बहुत सरल है, हालांकि, यह किसी भी asyncविधि पर काम करने के लिए अतिभारित है जो वापसी Task, Task<T>याTask<HttpResponseMessage> ... सिंथेटिक चीनी, बेबी ... डैडी को एक मीठा दाँत मिला है।

ऐसा लगता ...GetAwaiter().GetResult()है कि एक अवरुद्ध संदर्भ में async कोड निष्पादित करने के लिए एमएस-आधिकारिक तरीका है। मेरे उपयोग के मामले के लिए बहुत अच्छा काम करता है।


3
आपने मुझे "पूरी तरह से ब्लॉक की तरह" दिया था।
दाऊद इब्न करीम

9
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

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

var result=result.GetAwaiter().GetResult().AccessToken

6

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

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

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


5
तब यह विधि को सिंक्रोनाइज़ नहीं किया जाएगा अब यह होगा?
जेफ मर्काडो

2
जैसा कि मैं समझता हूं, सवाल यह था कि क्या आप एक गैर-एएसक्यूएन विधि से एक async विधि कह सकते हैं। यह ब्लॉकिंग तरीके से async विधि को कॉल करने का अर्थ नहीं है।
बेस 2

क्षमा करें, आपके "उन्हें asyncभी चिह्नित किया जाना चाहिए " ने मेरा ध्यान उस ओर खींचा जो आप वास्तव में कह रहे थे।
जेफ मर्काडो

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

6

मुझे पता है मुझे इतनी देर हो चुकी है। लेकिन अगर मेरे जैसा कोई व्यक्ति किसी दूसरे पुस्तकालय के आधार पर इसे साफ-सुथरे, आसान तरीके से हल करना चाहता है।

मुझे निम्नलिखित कोड रायन से मिला

public static class AsyncHelpers
{
    private static readonly TaskFactory taskFactory = new
        TaskFactory(CancellationToken.None,
            TaskCreationOptions.None,
            TaskContinuationOptions.None,
            TaskScheduler.Default);

    /// <summary>
    /// Executes an async Task method which has a void return value synchronously
    /// USAGE: AsyncUtil.RunSync(() => AsyncMethod());
    /// </summary>
    /// <param name="task">Task method to execute</param>
    public static void RunSync(Func<Task> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    /// <summary>
    /// Executes an async Task<T> method which has a T return type synchronously
    /// USAGE: T result = AsyncUtil.RunSync(() => AsyncMethod<T>());
    /// </summary>
    /// <typeparam name="TResult">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static TResult RunSync<TResult>(Func<Task<TResult>> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

तो आप इसे इस तरह से कॉल कर सकते हैं

var t = AsyncUtil.RunSync<T>(() => AsyncMethod<T>());

6
यह बिल्कुल ऐसा लगता है कि उपरोक्त उत्तर मुझे कुछ याद आ रहा है
Inlokesh

2

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

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

रिटर्न टास्क के साथ काम करता है। GetAwaiter (); GetResult ();
प्रति जी

हां, लेकिन मूल अपवाद के बारे में क्या?
जिरी हर्नरिक

.Result मुझे लगता है कि मूल रूप से .GetAwaiter () के समान ही है। GetResult ()
Per G

-2

इसे नए थ्रेड से बुलाया जा सकता है (थ्रेड पूल से नहीं!):

public static class SomeHelperClass
{ 
       public static T Result<T>(Func<T> func)
        {
            return Task.Factory.StartNew<T>(
                  () => func()
                , TaskCreationOptions.LongRunning
                ).Result;
        }
}
...
content = SomeHelperClass.Result<string>(
  () => response.Content.ReadAsStringAsync().Result
  );

-3

उन विंडो async मेथड में निफ्टी कम विधि होती है जिसे 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 
    // .....
}

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


-4

अगर आप इसे सिंक चलाना चाहते हैं

MethodAsync().RunSynchronously()

3
यह विधि ठंडे कार्यों को शुरू करने के लिए है। आमतौर पर async विधियाँ एक गर्म कार्य को वापस करती हैं, दूसरे शब्दों में एक कार्य जो पहले ही शुरू हो चुका है। RunSynchronously()हॉट टास्क पर कॉल करने से परिणाम ए InvalidOperationException। इसे इस कोड के साथ आज़माएँ:Task.Run(() => {}).RunSynchronously();
थियोडोर ज़ूलियस

-5
   //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;

2
गतिरोध उत्पन्न करें। बेहतर उत्तर हटा दें।
प्रीग्टनटनकोजेरो कैब्रॉन

Task.Run () => SaveAssetDataAsDraft ()) परिणाम; - गतिरोध उत्पन्न नहीं करता है
Anubis
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.