क्या मुझे इस बारे में चिंता करनी चाहिए कि "इस एसिंक्स विधि में 'प्रतीक्षित' परिचालकों की कमी है और यह समकालिक रूप से चलेगा"


95

मेरे पास एक इंटरफ़ेस है जो कुछ async विधियों को उजागर करता है। अधिक विशेष रूप से इसमें ऐसी विधियां हैं जो टास्क या टास्क <टी> को लौटाती हैं। मैं एसिंक्स / वेट कीवर्ड का उपयोग कर रहा हूं।

मैं इस इंटरफ़ेस को लागू करने की प्रक्रिया में हूं। हालाँकि, इनमें से कुछ तरीकों में इस कार्यान्वयन के लिए प्रतीक्षा करने के लिए कुछ भी नहीं है। इस कारण से मुझे कंपाइलर की चेतावनी मिल रही है "इस एस्किंस मेथड में 'वेट' ऑपरेटर्स की कमी है और यह सिंक्रोनाइज़ हो जाएगा ..."

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

मुझे पता है कि मैं एक टास्क पर प्रतीक्षा करके इसे ठीक कर सकता हूं। लेकिन यह एक ऐसी विधि के लिए गलत लगता है जो केवल कुछ सस्ती कार्रवाई कर रही है। यह भी लगता है कि यह निष्पादन में अनावश्यक ओवरहेड जोड़ देगा, लेकिन फिर मुझे भी यकीन नहीं है कि अगर यह पहले से ही है क्योंकि एसिंक्स कीवर्ड मौजूद है।

क्या मुझे सिर्फ चेतावनियों को नजरअंदाज करना चाहिए या क्या इसके आसपास काम करने का एक तरीका है जो मैं नहीं देख रहा हूं?


2
यह बारीकियों पर निर्भर है। क्या आप वास्तव में सुनिश्चित हैं कि आप चाहते हैं कि ये ऑपरेशन सिंक्रोनाइज़ किए जाएं? यदि आप चाहते हैं कि उन्हें समकालिक रूप से निष्पादित किया जाए, तो विधि को चिह्नित क्यों किया जाता है async?
सर्व

11
बस asyncकीवर्ड को हटा दें । आप अभी भी एक Taskप्रयोग करके लौट सकते हैं Task.FromResult
माइकल लियू

1
@BenVoigt Google इस बारे में जानकारी से भरा है, इस घटना में कि ओपी पहले से ही नहीं जानता है।
सेरी

1
@BenVoigt माइकल लियू ने पहले ही संकेत नहीं दिया था? का उपयोग करें Task.FromResult

1
@hvd: जिसे बाद में उनकी टिप्पणी में संपादित किया गया था।
बेन वोइग्ट

जवाबों:


147

Async कीवर्ड महज एक विधि के एक कार्यान्वयन विस्तार है, यह विधि हस्ताक्षर का हिस्सा नहीं है। एक विशेष विधि कार्यान्वयन या ओवरराइड का इंतजार करना कुछ भी नहीं है, तो बस छोड़ async कीवर्ड और का उपयोग कर एक पूर्ण कार्य लौट Task.FromResult <TResult> :

public Task<string> Foo()               //    public async Task<string> Foo()
{                                       //    {
    Baz();                              //        Baz();
    return Task.FromResult("Hello");    //        return "Hello";
}                                       //    }

यदि आपका तरीका टास्क <TResult> के बजाय टास्क लौटाता है , तो आप किसी भी प्रकार और मूल्य के पूर्ण कार्य को वापस कर सकते हैं। एक लोकप्रिय विकल्प लगता है:Task.FromResult(0)

public Task Bar()                       //    public async Task Bar()
{                                       //    {
    Baz();                              //        Baz();
    return Task.FromResult(0);          //
}                                       //    }

या, .नेट फ्रेमवर्क 4.6 के रूप में, आप लौट सकते हैं Task.CompletedTask :

public Task Bar()                       //    public async Task Bar()
{                                       //    {
    Baz();                              //        Baz();
    return Task.CompletedTask;          //
}                                       //    }

धन्यवाद मुझे लगता है कि जो मुझे याद आ रहा था वह एक टास्क बनाने की अवधारणा थी जो एक वास्तविक कार्य को वापस करने के बजाय पूरी हुई थी, जैसा कि आप कहते हैं कि यह async कीवर्ड होने के समान होगा। अब स्पष्ट लगता है, लेकिन मैं अभी इसे देख नहीं रहा था!
dannykay1710

1
टास्क इस काम के लिए टास्क की लाइनों के साथ एक स्थिर सदस्य के साथ कर सकता है। इरादा थोड़ा स्पष्ट होगा और यह मुझे उन सभी कर्तव्यपरायण कार्यो के बारे में सोचने को मजबूर करता है जो एक शून्य की वापसी करते हैं जिसकी कभी आवश्यकता नहीं होती है।
रूपर्ट रॉन्सली

await Task.FromResult(0)? कैसे के बारे में await Task.Yield()?
सुशी 271

1
@ सुशी 271: नहीं, गैर- asyncविधि में, आप इसका इंतजार करने के बजाय वापस लौटते हैं Task.FromResult(0)
माइकल लियू

1
वास्तव में NO, async सिर्फ एक कार्यान्वयन विवरण नहीं है, कई विवरण हैं जिनके बारे में एक को पता होना चाहिए :)। किसी को पता होना चाहिए कि कौन सा हिस्सा सिंक्रोनाइज़ करता है, कौन सा हिस्सा एसिंक्रोनस रूप से चलता है, वर्तमान सिंक्रोनाइज़ेशन संदर्भ क्या है और सिर्फ रिकॉर्ड के लिए, टास्क हमेशा बहुत कम होते हैं, क्योंकि पर्दे के पीछे राज्य मशीन नहीं है :)।
आईपावलू

17

यह पूरी तरह से उचित है कि कुछ "अतुल्यकालिक" ऑपरेशन पूरी तरह से समकालिक हैं, फिर भी बहुरूपता के लिए अतुल्यकालिक कॉल मॉडल के अनुरूप हैं।

इसका वास्तविक विश्व उदाहरण OS I / O एपीआई के साथ है। कुछ उपकरणों पर अतुल्यकालिक और अतिव्याप्त कॉल हमेशा इनलाइन को पूरा करती हैं (उदाहरण के लिए साझा मेमोरी का उपयोग करके कार्यान्वित पाइप पर लिखना)। लेकिन वे एक ही इंटरफ़ेस को बहु-भाग संचालन के रूप में लागू करते हैं जो पृष्ठभूमि में जारी रहता है।


4

माइकल लियू ने आपके सवाल का अच्छी तरह से जवाब दिया कि आप चेतावनी से कैसे बच सकते हैं: Task.FromResult को वापस करके।

मैं आपके प्रश्न के "क्या मुझे चेतावनी के बारे में चिंता करनी चाहिए" का जवाब देने जा रहा हूं।

इसका जवाब है हाँ!

इसका कारण यह है कि चेतावनी अक्सर तब आती है जब आप एक ऐसी विधि कहते हैं Taskजो awaitऑपरेटर के बिना एक async विधि के अंदर वापस आती है । मैंने सिर्फ एक संगोष्ठी बग तय की, क्योंकि मैंने पिछले ऑपरेशन की प्रतीक्षा किए बिना एंटिटी फ्रेमवर्क में एक ऑपरेशन को लागू किया था।

यदि आप संकलक चेतावनी से बचने के लिए सावधानीपूर्वक अपना कोड लिख सकते हैं, तो जब कोई चेतावनी होगी, तो यह एक गले में अंगूठे की तरह बाहर खड़ा होगा। मैं कई घंटों की डिबगिंग से बच सकता था।


5
यह उत्तर गलत है। यहाँ क्यों है: awaitएक जगह में विधि के अंदर कम से कम एक हो सकता है (कोई CS1998 नहीं होगा) लेकिन इसका मतलब यह नहीं है कि असनिक विधि का कोई अन्य कॉल नहीं होगा जिसमें सिंक्रनाइज़ेशन (उपयोग awaitया किसी अन्य) का अभाव होगा । अब अगर कोई यह जानना चाहता है कि कैसे आप गलती से सिंक्रनाइज़ेशन को याद नहीं करते हैं, तो सुनिश्चित करें कि आप एक और चेतावनी को अनदेखा न करें - CS4014। मैं भी धमकी देने की सिफारिश करूंगा कि एक त्रुटि के रूप में।
विक्टर यारेमा

4

यह बहुत देर हो सकती है लेकिन यह उपयोगी जांच हो सकती है:

संकलित कोड ( IL ) की आंतरिक संरचना के बारे में है :

 public static async Task<int> GetTestData()
    {
        return 12;
    }

यह IL में हो जाता है:

.method private hidebysig static class [mscorlib]System.Threading.Tasks.Task`1<int32> 
        GetTestData() cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 28 55 73 61 67 65 4C 69 62 72 61 72 79 2E   // ..(UsageLibrary.
                                                                                                                                     53 74 61 72 74 54 79 70 65 2B 3C 47 65 74 54 65   // StartType+<GetTe
                                                                                                                                     73 74 44 61 74 61 3E 64 5F 5F 31 00 00 )          // stData>d__1..
  .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       52 (0x34)
  .maxstack  2
  .locals init ([0] class UsageLibrary.StartType/'<GetTestData>d__1' V_0,
           [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> V_1)
  IL_0000:  newobj     instance void UsageLibrary.StartType/'<GetTestData>d__1'::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
  IL_000c:  stfld      valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
  IL_0011:  ldloc.0
  IL_0012:  ldc.i4.m1
  IL_0013:  stfld      int32 UsageLibrary.StartType/'<GetTestData>d__1'::'<>1__state'
  IL_0018:  ldloc.0
  IL_0019:  ldfld      valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
  IL_001e:  stloc.1
  IL_001f:  ldloca.s   V_1
  IL_0021:  ldloca.s   V_0
  IL_0023:  call       instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class UsageLibrary.StartType/'<GetTestData>d__1'>(!!0&)
  IL_0028:  ldloc.0
  IL_0029:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
  IL_002e:  call       instance class [mscorlib]System.Threading.Tasks.Task`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
  IL_0033:  ret
} // end of method StartType::GetTestData

और async और कार्य विधि के बिना:

 public static int GetTestData()
        {
            return 12;
        }

बन जाता है:

.method private hidebysig static int32  GetTestData() cil managed
{
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] int32 V_0)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   12
  IL_0003:  stloc.0
  IL_0004:  br.s       IL_0006
  IL_0006:  ldloc.0
  IL_0007:  ret
} // end of method StartType::GetTestData

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

अपडेट किया गया:

Microsoft डॉक्स https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth से अतिरिक्त जानकारी भी है :

async विधियों के लिए उनके शरीर में एक प्रतीक्षित खोजशब्द की आवश्यकता है या वे कभी उपज नहीं देंगे! यह ध्यान रखना जरूरी है। यदि एसिटिक विधि के शरीर में वाट्स का उपयोग नहीं किया जाता है, तो सी # कंपाइलर एक चेतावनी उत्पन्न करेगा, लेकिन कोड एक सामान्य विधि के रूप में संकलित और चलेगा। ध्यान दें कि यह भी अविश्वसनीय रूप से अक्षम होगा, क्योंकि एसिंक्स विधि के लिए सी # संकलक द्वारा उत्पन्न राज्य मशीन कुछ भी पूरा नहीं करेगी।


2
Additonally के उपयोग पर आपका अंतिम निष्कर्ष async/awaitकाफी हद तक निरीक्षण किया गया है क्योंकि आप इसे एक एकल ऑपरेशन के अपने अवास्तविक उदाहरण पर आधारित कर रहे हैं जो कि सीपीयू-बाउंड है। Taskएस जब समवर्ती कार्यों (यानी समानांतर) और थ्रेड्स के बेहतर प्रबंधन और बेहतर उपयोग के कारण बेहतर प्रदर्शन और जवाबदेही के लिए उपयोग किया जाता है
मिक्यद

जैसा कि मैंने इस पोस्ट में कहा था कि यह केवल सरलीकृत उदाहरण है। इसके अलावा, मैंने एपीआई और इवेंट हेंडलर के अनुरोधों के बारे में उल्लेख किया है, जहां दोनों तरीकों (async और नियमित) के संस्करण का उपयोग करके पॉसिबल है। इसके अलावा पीओ ने अंदर इंतजार किए बिना async विधियों का उपयोग करने के बारे में कहा। मेरी पोस्ट इसके बारे में थी लेकिन ठीक से उपयोग करने के बारे में नहीं Tasks। यह दुखद कहानी है कि आप पोस्ट के पूरे पाठ को नहीं पढ़ रहे हैं और जल्दी से निष्कर्ष निकाल रहे हैं।
ओलेग बोंडारेंको

2
एक विधि के बीच एक अंतर है जो रिटर्न करता है int(जैसा कि आपके मामले में) और एक जो Taskओपी द्वारा चर्चा के रूप में रिटर्न करता है। व्यक्तिगत रूप से चीजें लेने के बजाय उनके पोस्ट और स्वीकृत उत्तर को फिर से पढ़ें । आपका उत्तर इस मामले में मददगार नहीं है। तुम भी एक विधि है कि awaitअंदर है या नहीं के बीच अंतर दिखाने के लिए परेशान नहीं करते। अब तुमने किया था कि एक बहुत अच्छी तरह से एक upvote के लायक होता
मिक्यद

मुझे लगता है कि आप वास्तव में async विधि और नियमित लोगों के बीच अंतर को नहीं समझते हैं जिन्हें एपीआई या ईवेंट हैंडलर के साथ कहा जाता है। यह मेरे पोस्ट में विशेष रूप से उल्लेख किया गया था। आपके लिए खेद है कि फिर से याद आ रही है ।
ओलेग बोंडारेंको

1

लौटते समय अपवाद व्यवहार पर ध्यान दें Task.FromResult

यहां थोड़ा डेमो है जो चिह्नित किए गए तरीकों के बीच अपवाद हैंडलिंग में अंतर दिखाता है और चिह्नित नहीं है async

public Task<string> GetToken1WithoutAsync() => throw new Exception("Ex1!");

// Warning: This async method lacks 'await' operators and will run synchronously. Consider ...
public async Task<string> GetToken2WithAsync() => throw new Exception("Ex2!");  

public string GetToken3Throws() => throw new Exception("Ex3!");
public async Task<string> GetToken3WithAsync() => await Task.Run(GetToken3Throws);

public async Task<string> GetToken4WithAsync() { throw new Exception("Ex4!"); return await Task.FromResult("X");} 


public static async Task Main(string[] args)
{
    var p = new Program();

    try { var task1 = p.GetToken1WithoutAsync(); } 
    catch( Exception ) { Console.WriteLine("Throws before await.");};

    var task2 = p.GetToken2WithAsync(); // Does not throw;
    try { var token2 = await task2; } 
    catch( Exception ) { Console.WriteLine("Throws on await.");};

    var task3 = p.GetToken3WithAsync(); // Does not throw;
    try { var token3 = await task3; } 
    catch( Exception ) { Console.WriteLine("Throws on await.");};

    var task4 = p.GetToken4WithAsync(); // Does not throw;
    try { var token4 = await task4; } 
    catch( Exception ) { Console.WriteLine("Throws on await.");};
}
// .NETCoreApp,Version=v3.0
Throws before await.
Throws on await.
Throws on await.
Throws on await.

( जब इंटरफ़ेस के लिए आवश्यक async टास्क <T> के लिए मेरे उत्तर की क्रॉस पोस्ट , संकलक चेतावनी के बिना रिटर्न चर कैसे प्राप्त करें )

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.