c# - एक async कॉल का इंतजार नहीं अभी भी async है, है ना?
.net asynchronous (2)
मुझे खेद है कि यह एक मूर्खतापूर्ण प्रश्न है
यह एक मूर्खतापूर्ण सवाल नहीं है। यह एक महत्वपूर्ण प्रश्न है।
मेरे पास एक सहकर्मी है जो दावा करता है कि यह कॉल को ए समकालिक बनाता है और वह कंसोल के साथ आ रहा है। लाइटलाइन लॉग जो प्रतीत होता है कि उसकी बात साबित होती है।
यह वहीं मौलिक समस्या है, और आपको अपने सहकर्मी को शिक्षित करने की आवश्यकता है ताकि वे खुद को और दूसरों को गुमराह करना बंद कर दें। अतुल्यकालिक कॉल जैसी कोई चीज नहीं है । कॉल वह चीज नहीं है जो अतुल्यकालिक हो, कभी भी । मेरे साथ कहो। C # में कॉल अतुल्यकालिक नहीं हैं । C # में, जब आप किसी फ़ंक्शन को कॉल करते हैं, तो उस फ़ंक्शन को सभी तर्कों की गणना के तुरंत बाद कहा जाता है ।
यदि आपका सहकर्मी या आप मानते हैं कि एक एसिंक्रोनस कॉल के रूप में ऐसी कोई चीज है, तो आप दर्द की दुनिया के लिए हैं क्योंकि असिंक्रोनस कैसे काम करता है, इस बारे में आपका विश्वास वास्तविकता से बहुत दूर हो जाएगा।
तो, क्या आपका सहकर्मी सही है?
बेशक वे कर रहे हैं।
A
लिए कॉल सिंक्रोनस है क्योंकि सभी फ़ंक्शन कॉल सिंक्रोनस हैं
।
लेकिन तथ्य यह है कि उनका मानना है कि "अतुल्यकालिक कॉल" के रूप में ऐसी कोई चीज है, जिसका अर्थ है कि वे बुरी तरह से गलत हैं कि कैसे एसिंक्रोनसी सी # में काम करता है।
यदि विशेष रूप से आपके सहकर्मी का मानना है कि
await M()
किसी तरह
M()
"अतुल्यकालिक" कहती है, तो आपके सहकर्मी को एक बड़ी गलतफहमी है।
await
एक
ऑपरेटर है
।
यह सुनिश्चित करने के लिए एक जटिल ऑपरेटर है, लेकिन यह एक ऑपरेटर है, और यह मूल्यों पर काम करता है।
await M()
और
var t = M(); await t;
var t = M(); await t;
एक ही बात है
।
प्रतीक्षा कॉल के
बाद
होती है क्योंकि
await
उस मान पर काम करती है जो लौटाया जाता है
।
await
करना कंपाइलर को "एम ()" या ऐसी किसी भी चीज के लिए एक अतुल्यकालिक कॉल उत्पन्न करने का निर्देश
नहीं
है;
"अतुल्यकालिक कॉल" जैसी कोई चीज नहीं है।
अगर यह उनके झूठे विश्वास की प्रकृति है, तो आपको अपने सहकर्मी को शिक्षित करने का मौका मिला है कि इसका क्या अर्थ है।
await
मतलब कुछ सरल लेकिन शक्तिशाली है।
इसका मतलब:
-
उस
Task
जिसे मैं संचालित कर रहा हूं। - यदि कार्य असाधारण रूप से पूरा हो गया है, तो उस अपवाद को फेंक दें
- यदि कार्य सामान्य रूप से पूरा हो गया है, तो उस मूल्य को निकालें और उसका उपयोग करें
-
यदि कार्य अधूरा है, तो इस कार्य के शेष भाग को प्रतीक्षित कार्य की निरंतरता के रूप में साइन अप करें, और
इस कॉल के
अपूर्ण एसिंक्रोनस वर्कफ़्लो को
मेरे कॉलर
को दर्शाने वाला एक
नया
Task
लौटाएँ ।
बस यही
await
है।
यह सिर्फ एक कार्य की सामग्री की जांच करता है, और यदि कार्य अधूरा है, तो यह कहता है, "ठीक है, हम इस वर्कफ़्लो पर कोई प्रगति नहीं कर सकते जब तक कि कार्य पूरा न हो जाए, इसलिए मेरे कॉलर पर वापस लौटें, जो इस सीपीयू के लिए कुछ और खोजेगा करने के लिए"।
A के अंदर कोड की प्रकृति केवल इसलिए नहीं बदलती है क्योंकि हम इसकी प्रतीक्षा नहीं करते हैं।
यह सही है।
हम सिंक्रोनस
A
कहते हैं, और यह एक
Task
देता है।
कॉल साइट के बाद का कोड
A
वापस नहीं आता है।
A
बारे में दिलचस्प बात यह है कि
A
को अपने कॉलर को एक अधूरा
Task
वापस करने की अनुमति है
, और यह कार्य
एक अतुल्यकालिक वर्कफ़्लो में नोड का
प्रतिनिधित्व करता
है
।
वर्कफ़्लो
पहले
से
ही
अतुल्यकालिक है, और जैसा कि आप ध्यान दें, इससे
A
को कोई फ़र्क नहीं पड़ता कि आप उसके लौटने के
बाद
उसके रिटर्न वैल्यू के साथ क्या करते हैं;
A
का कोई पता नहीं है कि आप लौटे हुए
Task
का
await
करने जा रहे हैं या नहीं।
जब तक यह कर सकता है, तब तक बस चलता है, और फिर या तो यह एक पूर्ण-सामान्य कार्य, या पूर्ण-असाधारण कार्य देता है, या यह एक अपूर्ण कार्य देता है।
लेकिन कॉल साइट पर आप जो कुछ भी करते हैं, वह बदल जाता है।
चूंकि A से वापसी मूल्य की आवश्यकता नहीं है, इसलिए कॉल साइट पर कार्य का इंतजार करने की कोई आवश्यकता नहीं है
सही बात।
कॉल साइट पर कार्य का इंतजार करने की कोई आवश्यकता नहीं है, जब तक कि कोई व्यक्ति श्रृंखला का इंतजार नहीं करता है (जो सी में होता है)।
अब तुम मुझे खो चुके हो।
किसी
को
A
द्वारा लौटाए गए
Task
का इंतजार क्यों करना पड़ता है?
यह कहें कि आप क्यों मानते हैं कि किसी को उस
Task
await
करने की
आवश्यकता
है, क्योंकि आपके पास एक गलत विश्वास हो सकता है।
मेरा सहकर्मी बहुत जिद कर रहा है और मुझे खुद पर शक होने लगा है। क्या मेरी समझ गलत है?
आपका सहकर्मी लगभग निश्चित रूप से गलत है।
आपका विश्लेषण सही पर सही लगता है जहां आप कहते हैं कि एक
आवश्यकता है
कि प्रत्येक
Task
का
await
किया जाना चाहिए, जो सच नहीं है।
Task
await
न करना
अजीब
है क्योंकि इसका मतलब है कि आपने एक प्रोग्राम लिखा है, जहां आपने एक ऑपरेशन शुरू किया है और इस बात की परवाह नहीं है कि यह कब और कैसे पूरा होता है, और यह निश्चित रूप से इस तरह के प्रोग्राम को लिखने के लिए
खराब खुशबू आ रही
है, लेकिन इसकी
आवश्यकता
नहीं है हर
Task
का
await
यदि आप मानते हैं कि फिर से, कहना है कि विश्वास क्या है और हम इसे सुलझा लेंगे।
मुझे खेद है अगर यह एक मूर्खतापूर्ण सवाल है (या डुप्लिकेट)।
मेरे पास एक फ़ंक्शन है:
public async Task<int> A(/* some parameters */)
{
var result = await SomeOtherFuncAsync(/* some other parameters */);
return (result);
}
मेरे पास एक और फ़ंक्शन
B
,
A
कॉल करना लेकिन रिटर्न वैल्यू का उपयोग नहीं करना:
public Task B(/* some parameters */)
{
var taskA = A(/* parameters */); // #1
return (taskA);
}
ध्यान दें कि
B
को
async
घोषित नहीं किया गया है और
A
को कॉल का इंतजार नहीं है।
A
को कॉल करना आग और भूल कॉल नहीं है -
B
को
C
द्वारा कॉल किया जाता है:
public async Task C()
{
await B(/* parameters */);
}
ध्यान दें कि
# 1 पर
, कोई
await
नहीं है।
मेरे पास एक सहकर्मी है जो दावा करता है कि यह कॉल को
A
समकालिक बनाता है और वह
Console.WriteLine
साथ आ रहा है।
Console.WriteLine
लॉग जो प्रतीत होता है कि उसकी बात साबित होती है।
मैंने यह इंगित करने का प्रयास किया कि सिर्फ इसलिए कि हम
B
अंदर परिणामों की प्रतीक्षा नहीं करते हैं, कार्य श्रृंखला प्रतीक्षित है और
A
के अंदर कोड की प्रकृति सिर्फ इसलिए नहीं बदलती है क्योंकि हम इसकी प्रतीक्षा नहीं करते हैं।
चूंकि
A
से वापसी मूल्य की आवश्यकता नहीं है, इसलिए कॉल साइट पर कार्य का इंतजार करने की आवश्यकता नहीं है, जब तक
कि श्रृंखला के किसी व्यक्ति को इंतजार नहीं
होता है (जो
C
में होता
C
)।
मेरा सहकर्मी बहुत जिद कर रहा है और मुझे खुद पर शक होने लगा है। क्या मेरी समझ गलत है?
तुम सही हो।
एक कार्य का निर्माण केवल वही करता है और यह परवाह नहीं करता है कि कब और कौन इसके परिणाम की प्रतीक्षा करेगा।
await Task.Delay(veryBigNumber);
प्रयास करें
await Task.Delay(veryBigNumber);
SomeOtherFuncAsync
और कंसोल आउटपुट में
SomeOtherFuncAsync
होना चाहिए जो आप अपेक्षा करेंगे।
इसे एलडिंग कहा जाता है और मेरा सुझाव है कि आप इस ब्लॉगपोस्ट को पढ़ें, जहां आप देख सकते हैं कि आपको ऐसा क्यों करना चाहिए या नहीं करना चाहिए।
आपके कोड को सही साबित करते हुए आपके कोड की नकल करने वाले कुछ न्यूनतम (थोड़ा जटिल) उदाहरण:
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Start of main {Thread.CurrentThread.ManagedThreadId}");
var task = First();
Console.WriteLine($"Middle of main {Thread.CurrentThread.ManagedThreadId}");
await task;
Console.WriteLine($"End of main {Thread.CurrentThread.ManagedThreadId}");
}
static Task First()
{
return SecondAsync();
}
static async Task SecondAsync()
{
await ThirdAsync();
}
static async Task ThirdAsync()
{
Console.WriteLine($"Start of third {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(1000);
Console.WriteLine($"End of third {Thread.CurrentThread.ManagedThreadId}");
}
}
यह
End of third
पहले
Middle of main
लिखता है, यह साबित करता है कि यह वास्तव में अतुल्यकालिक है।
फुरथ्मोर आप (सबसे अधिक संभावना है) देख सकते हैं कि फ़ंक्शन के सिरों को बाकी प्रोग्राम की तुलना में अलग-अलग थ्रेड पर चलाया जाता है।
दोनों शुरुआत और मुख्य का मध्य हमेशा एक ही धागे पर चलेगा क्योंकि वे वास्तव में समानार्थी हैं (मुख्य शुरू होता है, फ़ंक्शन चेन को कॉल करता है, तीसरा रिटर्न (यह
await
कीवर्ड के साथ लाइन में वापस आ सकता है) और फिर मुख्य जारी रहता है जैसे कि वहाँ कोई अतुल्यकालिक फ़ंक्शन शामिल नहीं था। दोनों फ़ंक्शन में
await
कीवर्ड के बाद समाप्त थ्रेडपूल (या आपके द्वारा उपयोग किए जा रहे सिंक्रनाइज़ेशन संदर्भ) में किसी भी थ्रेड पर चल सकता है।
अब यह नोट करना दिलचस्प है, कि यदि
Task.Delay
इन
Third
में बहुत लंबा समय नहीं लगा और वास्तव में सिंक्रोनस समाप्त हो गया, तो यह सब एक ही धागे पर चलेगा।
क्या अधिक है, भले ही यह अतुल्यकालिक रूप से चलेगा, यह सब एक ही धागे पर चल
सकता है
।
यह बताते हुए कोई नियम नहीं है कि एक async फ़ंक्शन एक से अधिक थ्रेड का उपयोग करेगा, यह बहुत अच्छी तरह से बस कुछ अन्य काम कर सकता है जबकि कुछ I / O कार्य समाप्त होने की प्रतीक्षा कर रहा है।