सिंक्रोनस विधि को सिंक्रोनाइज़ करना


231

मेरे पास एक asyncविधि है:

public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

मुझे इस विधि को एक तुल्यकालिक विधि से कॉल करने की आवश्यकता है।

मैं कैसे GenerateCodeAsyncतुल्यकालिक रूप से काम करने के लिए इस विधि की नकल किए बिना ऐसा कर सकता हूं ?

अपडेट करें

फिर भी कोई उचित समाधान नहीं मिला।

हालाँकि, मुझे लगता है कि HttpClientपहले से ही इस पैटर्न को लागू करता है

using (HttpClient client = new HttpClient())
{
    // async
    HttpResponseMessage responseAsync = await client.GetAsync(url);

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}


1
मैं एक सरल समाधान के लिए उम्मीद की गई थी, कि asp.net सोच संभाला इतना कोड के इतने लाइनों लेखन की तुलना में आसान
Catalin

सिर्फ async कोड क्यों नहीं अपनाया? आदर्श रूप से आप अधिक async कोड चाहते हैं, कम नहीं।
पाउलो मोर्गेडो

53
[सिर्फ async कोड क्यों नहीं?] हा, यह ठीक हो सकता है क्योंकि एक async कोड को गले लगा रहा है कि उन्हें इस समाधान की आवश्यकता है क्योंकि परियोजना के बड़े हिस्से परिवर्तित हो जाते हैं! आप एक दिन में रोम का पुनर्निर्माण नहीं कर सकते।
निकोलस पीटरसन

1
@ निचलोलपेटर्स कभी-कभी 3-पार्टी लाइब्रेरी आपको ऐसा करने के लिए मजबूर कर सकती है। फ्लुएंटवैलिडेशन के साथ विएसमेस मेथड में डायनामिक संदेशों का निर्माण। लाइब्रेरी डिज़ाइन के कारण इसके लिए कोई async API नहीं है - WithMessage ओवरलोड स्थिर हैं। WithMessage को गतिशील तर्क पास करने के अन्य तरीके अजीब हैं।
H.Wojtowicz

जवाबों:


278

आप Resultकार्य की संपत्ति तक पहुंच सकते हैं, जिसके परिणामस्वरूप परिणाम उपलब्ध होने तक आपका धागा अवरुद्ध हो जाएगा:

string code = GenerateCodeAsync().Result;

नोट: कुछ मामलों में, यह एक गतिरोध पैदा कर सकता है: आपका कॉल Resultमुख्य धागे को अवरुद्ध करता है, जिससे शेष Async कोड को निष्पादित करने से रोकता है। आपके पास यह सुनिश्चित करने के लिए निम्नलिखित विकल्प हैं कि ऐसा न हो:

इसका मतलब यह नहीं है कि आप .ConfigureAwait(false)अपने सभी async कॉल के बाद बस माइंडलेस तरीके से जोड़ें ! क्यों और कब उपयोग करना चाहिए .ConfigureAwait(false), इस पर विस्तृत विश्लेषण के लिए , निम्न ब्लॉग पोस्ट देखें:


33
प्रेरक हैं resultएक गतिरोध जोखिम, तो जब है यह सुरक्षित परिणाम प्राप्त करने के? क्या हर एसिंक्रोनस कॉल की आवश्यकता होती है Task.Runया ConfigureAwait(false)?
रॉबर्ट हार्वे

4
ASP.NET में कोई "मुख्य धागा" नहीं है (GUI ऐप के विपरीत), लेकिन गतिरोध अभी भी संभव है क्योंकि कैसे AspNetSynchronizationContext.Post async निरंतरता को क्रमबद्ध करता है:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
noseratio

4
@RobertHarvey: यदि आपके पास अवरुद्ध होने वाली एस्किंक विधि के कार्यान्वयन पर कोई नियंत्रण नहीं है, तो हाँ, आपको Task.Runसुरक्षित रहने के लिए इसे लपेटना चाहिए । या WithNoContextनिरर्थक थ्रेड स्विचिंग को कम करने के लिए कुछ का उपयोग करें ।
noseratio

10
नोट: कॉलिंग .Resultथ्रेड पूल पर ही है, तो कॉलिंग अभी भी गतिरोध कर सकती है। ऐसा परिदृश्य लें जहां थ्रेड पूल आकार 32 का हो और 32 कार्य चल रहे हों और Wait()/Resultप्रतीक्षा करने वाले 33 वें कार्य पर प्रतीक्षा कर रहे हों, जो प्रतीक्षा थ्रेड्स में से एक पर चलना चाहते हैं।
वार्ती

55

आपको GetAwaiter()प्रतीक्षाकर्ता ( ) प्राप्त करना चाहिए और अतुल्यकालिक कार्य ( GetResult()) के पूरा होने का इंतजार समाप्त करना चाहिए ।

string code = GenerateCodeAsync().GetAwaiter().GetResult();

37
हम इस समाधान का उपयोग करके गतिरोध में चले गए हैं। चेतावनी दी।
ओलिवर

6
MSDNTask.GetAwaiter : यह विधि अनुप्रयोग कोड में उपयोग के बजाय संकलक उपयोग के लिए अभिप्रेत है।
फॉक्स

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

30

आपको प्रतिनिधियों, लैम्ब्डा अभिव्यक्ति का उपयोग करके इसे पूरा करने में सक्षम होना चाहिए

private void button2_Click(object sender, EventArgs e)
    {

        label1.Text = "waiting....";

        Task<string> sCode = Task.Run(async () =>
        {
            string msg =await GenerateCodeAsync();
            return msg;
        });

        label1.Text += sCode.Result;

    }

    private Task<string> GenerateCodeAsync()
    {
        return Task.Run<string>(() => GenerateCode());
    }

    private string GenerateCode()
    {
        Thread.Sleep(2000);
        return "I m back" ;
    }

यह स्निपेट संकलन नहीं करेगा। Task.Run से रिटर्न प्रकार टास्क है। पूर्ण विवरण के लिए इस MSDN ब्लॉग को देखें ।
उपविजेता

5
इंगित करने के लिए धन्यवाद, हाँ यह टास्क प्रकार लौटाता है। "स्ट्रिंग sCode" को कार्य <string> या var sCode में बदलकर इसे हल करना चाहिए। आसानी के लिए एक पूर्ण संकलन कोड जोड़ना।
फैयाज

20

मुझे इस विधि को एक तुल्यकालिक विधि से कॉल करने की आवश्यकता है।

यह संभव है GenerateCodeAsync().Resultया GenerateCodeAsync().Wait(), जैसा कि अन्य उत्तर से पता चलता है। यह GenerateCodeAsyncपूरा होने तक वर्तमान थ्रेड को ब्लॉक कर देगा ।

हालाँकि, आपके प्रश्न का टैग लगा हुआ है , और आपने टिप्पणी भी छोड़ दी:

मैं एक सरल समाधान की उम्मीद कर रहा था, यह सोचकर कि asp.net ने कोड की इतनी सारी लाइनें लिखने की तुलना में यह बहुत आसान है

मेरा कहना है, आप ASP.NET में एक अतुल्यकालिक विधि पर अवरुद्ध नहीं होना चाहिए । यह आपके वेब ऐप की स्केलेबिलिटी को कम करेगा, और गतिरोध पैदा कर सकता है (जब एक awaitनिरंतरता GenerateCodeAsyncपोस्ट की जाती है AspNetSynchronizationContext)। Task.Run(...).Resultपूल थ्रेड में कुछ को लोड करने के लिए उपयोग करना और फिर स्केलेबिलिटी को और भी अधिक प्रभावित करेगा, क्योंकि यह दिए गए HTTP अनुरोध को संसाधित करने के लिए +1 अधिक थ्रेड को सम्मिलित करता है।

ASP.NET में अतुल्यकालिक विधियों के लिए अंतर्निहित समर्थन है, या तो अतुल्यकालिक नियंत्रकों (ASP.NET MVC और वेब API में) के माध्यम से या सीधे AsyncManagerऔर सीधे PageAsyncTaskASP.NET में। आपको इसका उपयोग करना चाहिए। अधिक जानकारी के लिए, इस उत्तर को देखें


मैं का अधिलेखित SaveChanges()करने का तरीका है DbContext, और यहाँ मैं async विधियों को बुला रहा हूँ, इसलिए दुर्भाग्य से async नियंत्रक इस स्थिति में मेरी मदद नहीं करेगा
कैटालिन

3
@ RaraituL, सामान्य रूप से, आप async और सिंक कोड नहीं मिलाते हैं, euther मॉडल चुनें। आप दोनों को लागू कर सकते हैं SaveChangesAsyncऔर SaveChanges, बस यह सुनिश्चित कर लें कि उन्हें एक ही ASP.NET प्रोजेक्ट में दोनों नहीं मिलते हैं।
noseratio

4
सभी .NET MVCफ़िल्टर अतुल्यकालिक कोड का समर्थन नहीं करते हैं, उदाहरण के लिए IAuthorizationFilter, इसलिए मैं asyncसभी तरह से उपयोग नहीं कर सकता हूं
कैटलिन

3
@ नोसेरियोटी एक अवास्तविक लक्ष्य है। एसिंक्रोनस और सिंक्रोनस कोड के साथ बहुत सारी लाइब्रेरी भी हैं और साथ ही ऐसी स्थितियाँ जिनमें केवल एक मॉडल का उपयोग करना संभव नहीं है। उदाहरण के लिए, MVC ActionFilters एसिंक्रोनस कोड का समर्थन नहीं करते हैं।
जस्टिन स्काइल्स

9
@ नोसेराटो, सवाल सिंक्रोनस से अतुल्यकालिक विधि को कॉल करने के बारे में है। कुछ समय के बाद आप एपीआई को लागू नहीं कर सकते। मान लें कि आप कुछ 3-आरडी पार्टी फ्रेमवर्क "ए" से कुछ सिंक्रोनस इंटरफ़ेस लागू कर रहे हैं (आप फ्रेमवर्क को एसिंक्रोनस तरीके से फिर से नहीं लिख सकते हैं) लेकिन 3-पार्टी लाइब्रेरी "बी" जिसे आप अपने कार्यान्वयन में उपयोग करने की कोशिश कर रहे हैं, में केवल एसिंक्रोनस है। परिणामी उत्पाद भी पुस्तकालय है और ASP.NET आदि सहित कहीं भी उपयोग किया जा सकता है
dimzon

19

Microsoft आइडेंटिटी में विस्तार विधियाँ होती हैं जो सिंक्रोनस विधियों को सिंक्रोनाइज़ करती हैं। उदाहरण के लिए GenerateUserIdentityAsync () विधि और समान CreateIdentity () है

यदि आप UserManagerExtensions.CreateIdentity () को देखते हैं तो यह इस प्रकार है:

 public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
        string authenticationType)
        where TKey : IEquatable<TKey>
        where TUser : class, IUser<TKey>
    {
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }
        return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
    }

अब देखते हैं कि AsyncHelper.RunSync क्या करता है

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

तो, यह async विधि के लिए आपका आवरण है। और कृपया परिणाम का डेटा न पढ़ें - यह संभवतः आपके कोड को ASP में ब्लॉक कर देगा।

एक और तरीका है - जो मेरे लिए संदिग्ध है, लेकिन आप इस पर भी विचार कर सकते हैं

  Result r = null;

            YourAsyncMethod()
                .ContinueWith(t =>
                {
                    r = t.Result;
                })
                .Wait();

3
आपके द्वारा सुझाए गए दूसरे तरीके के साथ आप इस मुद्दे को क्या मानते हैं?
डेविड क्लार्क

@DavidClarke एक लॉक के बिना कई थ्रेड्स से गैर-वाष्पशील चर को एक्सेस करने का थ्रेड सुरक्षा मुद्दा है।
थियोडोर जूलियास

9

डेडलॉक को रोकने के लिए मैं हमेशा उपयोग करने की कोशिश करता Task.Run()हूं जब मुझे एक async विधि को कॉल करना पड़ता है जो कि @Hezizi उल्लेख करता है।

हालाँकि, यदि async विधि मापदंडों का उपयोग करती है तो विधि को संशोधित करना होगा। उदाहरण के लिए Task.Run(GenerateCodeAsync("test")).Resultत्रुटि देता है:

तर्क 1: ' System.Threading.Tasks.Task<string>' से 'System.Action' में परिवर्तित नहीं हो सकता

इसके बजाय इसे इस तरह कहा जा सकता है:

string code = Task.Run(() => GenerateCodeAsync("test")).Result;

5

इस सूत्र पर अधिकांश उत्तर या तो जटिल हैं या परिणाम में गतिरोध होगा।

निम्नलिखित विधि सरल है और यह गतिरोध से बचेगी क्योंकि हम कार्य के समाप्त होने की प्रतीक्षा कर रहे हैं और केवल तभी इसका परिणाम मिल रहा है-

var task = Task.Run(() => GenerateCodeAsync()); 
task.Wait();
string code = task.Result;

इसके अलावा, यहाँ MSDN लेख का संदर्भ दिया गया है जो बिल्कुल एक ही बात करता है- https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result- इन-मुख्य संदर्भ /


1

मैं एक गैर अवरुद्ध दृष्टिकोण पसंद करते हैं:

            Dim aw1=GenerateCodeAsync().GetAwaiter()
            While Not aw1.IsCompleted
                Application.DoEvents()
            End While

0

वैसे मैं इस दृष्टिकोण का उपयोग कर रहा हूं:

    private string RunSync()
    {
        var task = Task.Run(async () => await GenerateCodeService.GenerateCodeAsync());
        if (task.IsFaulted && task.Exception != null)
        {
            throw task.Exception;
        }

        return task.Result;
    }

-1

दूसरा तरीका यह हो सकता है कि यदि आप कार्य समाप्त होने तक प्रतीक्षा करना चाहते हैं:

var t = GenerateCodeService.GenerateCodeAsync();
Task.WhenAll(t);
string code = t.Result;

1
यह गलत है। जब सभी एक टास्क भी लौटा देंगे, जिसका आपको इंतजार नहीं है।
रॉबर्ट श्मिट

-1

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

टास्क में वेट मेथड है, Task.Wait (), जो हल करने के लिए "वादा" का इंतजार करता है और फिर जारी रखता है, इस प्रकार इसे सिंक्रोनस रेंडर करता है। उदाहरण:


async Task<String> MyAsyncMethod() { ... }

String mySyncMethod() {

    return MyAsyncMethod().Wait();
}

3
कृपया अपने उत्तर के बारे में विस्तार से बताएं। इसका उपयोग कैसे किया जा सकता है? प्रश्न का उत्तर देने में यह विशेष रूप से कैसे मदद करता है?
स्क्रेप्ट

-2

यदि आपके पास " रिफ्रेशलिस्ट " नामक एक async विधि है , तो आप नीचे दिए गए गैर-async विधि से उस async विधि को कॉल कर सकते हैं।

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