इकाई फ्रेमवर्क SaveChanges () बनाम SaveChangesAsync () और ढूँढें () बनाम FindAsync ()


86

मैं ऊपर 2 जोड़े के बीच के अंतरों की खोज कर रहा हूं, लेकिन किसी भी लेख को स्पष्ट रूप से इसके बारे में समझाने के साथ-साथ एक या दूसरे का उपयोग करने के लिए नहीं मिला है।

तो मध्य क्या अंतर है SaveChanges()और SaveChangesAsync()?
और के बीच Find()और FindAsync()?

सर्वर साइड पर, जब हम Asyncविधियों का उपयोग करते हैं, तो हमें भी जोड़ना होगा await। इस प्रकार, मुझे नहीं लगता कि यह सर्वर की तरफ अतुल्यकालिक है।

क्या यह केवल क्लाइंट साइड ब्राउज़र पर यूआई को रोकने में मदद करता है? या फिर उनके बीच कोई पेशेवरों और विपक्ष हैं?


2
async बहुत अधिक है, क्लाइंट अनुप्रयोगों में अवरुद्ध करने से क्लाइंट UI थ्रेड को रोकने से बहुत अधिक है। मुझे यकीन है कि शीघ्र ही एक विशेषज्ञ का जवाब आने वाला है।
jdphenix

जवाबों:


174

किसी भी समय आपको एक दूरस्थ सर्वर पर कार्रवाई करने की आवश्यकता होती है, आपका कार्यक्रम अनुरोध उत्पन्न करता है, भेजता है, फिर प्रतिक्रिया का इंतजार करता है। मैं का उपयोग करेगा SaveChanges()और SaveChangesAsync()एक उदाहरण के रूप लेकिन एक ही लागू होता है Find()और FindAsync()

मान लें कि आपके पास myList100 + आइटमों की एक सूची है , जिन्हें आपको अपने डेटाबेस में जोड़ना होगा। डालने के लिए, आपका कार्य कुछ इस तरह दिखाई देगा:

using(var context = new MyEDM())
{
    context.MyTable.AddRange(myList);
    context.SaveChanges();
}

सबसे पहले आप एक उदाहरण बनाते MyEDMहैं, सूची myListको तालिका में जोड़ते हैं MyTable, फिर SaveChanges()डेटाबेस में परिवर्तन जारी रखने के लिए कॉल करते हैं। यह काम करता है कि आप कैसे चाहते हैं, रिकॉर्ड प्रतिबद्ध हो जाते हैं, लेकिन जब तक कमिट खत्म नहीं हो जाता, तब तक आपका प्रोग्राम कुछ और नहीं कर सकता। आप जो कर रहे हैं उसके आधार पर यह एक लंबा समय ले सकता है। यदि आप रिकॉर्ड्स में बदलाव कर रहे हैं, तो इकाई को एक समय में एक (मुझे एक बार अपडेट के लिए 2 मिनट बचाने के लिए) प्रतिबद्ध करना होगा!

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

आई / ओ ओपेरशन के लिए, awaitजल्दी से आपका सबसे अच्छा दोस्त बन जाता है। ऊपर से कोड अनुभाग लेते हुए, हम इसे संशोधित कर सकते हैं:

using(var context = new MyEDM())
{
    Console.WriteLine("Save Starting");
    context.MyTable.AddRange(myList);
    await context.SaveChangesAsync();
    Console.WriteLine("Save Complete");
}

यह एक बहुत छोटा परिवर्तन है, लेकिन आपके कोड की दक्षता और प्रदर्शन पर गहरा प्रभाव पड़ता है। तो क्या होता है? कोड की भीख एक समान है, आप इसका एक उदाहरण बनाते हैं MyEDMऔर अपने myListको जोड़ते हैं MyTable। लेकिन जब आप कॉल करते हैं await context.SaveChangesAsync(), तो कोड का निष्पादन कॉलिंग फ़ंक्शन पर लौट जाता है! इसलिए जब आप उन सभी रिकॉर्डों के लिए इंतजार कर रहे हैं, तो आपका कोड निष्पादित करना जारी रख सकता है। उस फ़ंक्शन को कहें जिसमें उपरोक्त कोड का हस्ताक्षर था public async Task SaveRecords(List<MyTable> saveList), कॉलिंग फ़ंक्शन इस तरह दिख सकता है:

public async Task MyCallingFunction()
{
    Console.WriteLine("Function Starting");
    Task saveTask = SaveRecords(GenerateNewRecords());

    for(int i = 0; i < 1000; i++){
        Console.WriteLine("Continuing to execute!");
    }

    await saveTask;
    Console.Log("Function Complete");
}

आपके पास इस तरह का एक समारोह क्यों होगा, मुझे नहीं पता, लेकिन यह क्या आउटपुट दिखाता है कि कैसे async awaitकाम करता है। पहले जाने कि क्या होता है।

निष्पादन प्रवेश करता है MyCallingFunction, Function Startingफिर Save Startingकंसोल को लिखा जाता है, फिर फ़ंक्शन SaveChangesAsync()को कॉल किया जाता है। इस बिंदु पर, निष्पादन MyCallingFunction1000 बार तक लूप लेखन 'कंटीन्यूइंग टू एक्स्यूट्यूट' के लिए वापस आता है और प्रवेश करता है। जब SaveChangesAsync()खत्म हो जाता है, तो SaveRecordsफंक्शन Save Completeकंसोल पर लिखते हुए फंक्शन पर वापस आ जाता है। एक बार जब सब कुछ SaveRecordsपूरा हो जाता है, तो निष्पादन जारी रहेगा MyCallingFunctionजब यह SaveChangesAsync()समाप्त हो गया था । उलझन में? यहाँ एक उदाहरण आउटपुट है:

कार्य प्रारंभ करना
शुरू बचाओ
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
....
निरंतर निष्पादित करने के लिए!
पूरा बचाओ!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
....
निरंतर निष्पादित करने के लिए!
कार्य पूर्ण!

या हो सकता है:

कार्य प्रारंभ करना
शुरू बचाओ
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
पूरा बचाओ!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
निरंतर निष्पादित करने के लिए!
....
निरंतर निष्पादित करने के लिए!
कार्य पूर्ण!

यह सौंदर्य की बात है async await, जब आप किसी चीज के खत्म होने का इंतजार कर रहे हैं तो आपका कोड चलना जारी रख सकता है। वास्तव में, आपके पास अपने कॉलिंग फ़ंक्शन के रूप में इस तरह का एक फ़ंक्शन होगा:

public async Task MyCallingFunction()
{
    List<Task> myTasks = new List<Task>();
    myTasks.Add(SaveRecords(GenerateNewRecords()));
    myTasks.Add(SaveRecords2(GenerateNewRecords2()));
    myTasks.Add(SaveRecords3(GenerateNewRecords3()));
    myTasks.Add(SaveRecords4(GenerateNewRecords4()));

    await Task.WhenAll(myTasks.ToArray());
}

यहां, आपके पास एक ही समय में चार अलग-अलग सेव रिकॉर्ड फ़ंक्शन हैं । व्यक्तिगत कार्यों को श्रृंखला में बुलाए जाने की तुलना में MyCallingFunctionबहुत तेज़ी से पूरा होगा ।async awaitSaveRecords

एक चीज जिसे मैंने अभी तक नहीं छुआ है वह है awaitकीवर्ड। यह क्या करता है वर्तमान फ़ंक्शन को तब तक निष्पादित करने से रोकना है जब तक कि Taskआप जो भी पूरी होने का इंतजार कर रहे हैं। तो मूल के मामले में MyCallingFunction, लाइन Function Completeको कंसोल तक नहीं लिखा जाएगाSaveRecords फ़ंक्शन को खत्म होने ।

लंबी कहानी छोटी, यदि आपके पास उपयोग करने का विकल्प है async await, तो आपको ऐसा करना चाहिए क्योंकि यह आपके आवेदन के प्रदर्शन को बढ़ा देगा।


7
समय का 99% मुझे अभी भी डेटाबेस से प्राप्त होने से पहले मूल्यों के लिए इंतजार करना होगा। क्या मुझे अभी भी async का उपयोग करना चाहिए? क्या एसिंक्रोन 100 लोगों को मेरी वेबसाइट से अतुल्यकालिक रूप से जुड़ने की अनुमति देता है? यदि मैं async का उपयोग नहीं करता है तो इसका मतलब है कि सभी 100 उपयोगकर्ताओं को एक बार में लाइन 1 में इंतजार करना होगा?
जूल

6
वर्थ नोटिंग: थ्रेड पूल से एक नया धागा पैदा करना एएसपी को एक दुखद पांडा बनाता है क्योंकि आप मूल रूप से एएसपी से एक धागा लूटते हैं (इसका अर्थ है कि धागा अन्य अनुरोधों को संभाल नहीं सकता है या ऐसा कुछ भी नहीं कर सकता है क्योंकि यह अवरुद्ध कॉल में फंस गया है)। यदि आप awaitफिर भी उपयोग करते हैं , भले ही आपको SaveChanges के कॉल के बाद कुछ और करने की आवश्यकता न हो, ASP कहेंगे "अहा, यह धागा एक async ऑपरेशन की प्रतीक्षा में लौटा है, इसका मतलब है कि मैं इस थ्रेड को इस बीच कुछ अन्य अनुरोध को संभालने दे सकता हूं ! " यह आपके ऐप स्केल को क्षैतिज रूप से बहुत बेहतर बनाता है।
सारा

3
वास्तव में मैंने async को धीमा करने के लिए बेंचमार्क किया है। और क्या आपने कभी देखा है कि एक विशिष्ट ASP.Net सर्वर में कितने धागे उपलब्ध हैं? यह हजारों की तरह है। तो आगे के अनुरोधों को संभालने के लिए थ्रेड्स से बाहर निकलने की संभावना बहुत कम है और यहां तक ​​कि अगर आपके पास उन सभी थ्रेड्स को संतृप्त करने के लिए पर्याप्त ट्रैफ़िक है, तो क्या आपका सर्वर वास्तव में इतना शक्तिशाली है कि उस मामले में बकसुआ नहीं है? यह दावा करने के लिए कि हर जगह एस्क् का उपयोग करने से प्रदर्शन बढ़ता है पूरी तरह से गलत है। यह कुछ परिदृश्यों में हो सकता है, लेकिन ज्यादातर सामान्य स्थितियों में यह वास्तव में धीमा होगा। बेंचमार्क और देखें।
user3766657

@MIKE करते समय एक उपयोगकर्ता को डेटा जारी रखने के लिए डेटाबेस का इंतजार करना चाहिए, आपके अन्य उपयोगकर्ता आपके एप्लिकेशन का उपयोग नहीं करते। जबकि IIS प्रत्येक अनुरोध के लिए एक धागा बनाता है (वास्तव में इसकी तुलना में अधिक जटिल), आपके प्रतीक्षारत धागे का उपयोग अन्य अनुरोधों को संभालने के लिए किया जा सकता है, यह स्केलेबिलिटी afaik के लिए महत्वपूर्ण है। प्रत्येक अनुरोध को पूरा करने के बजाय, 1 थ्रेड पूर्णकालिक का उपयोग करने के बजाय यह कई छोटे थ्रेड्स का उपयोग करता है जिन्हें कहीं और पुन: उपयोग किया जा सकता है (उर्फ अन्य अनुरोध)।
बार्ट कैलिक्सो

1
मैं तो बस जोड़ने के लिए आप चाहते हैं कि हमेशा करना चाहिए await के लिए SaveChangesAsyncके बाद से एफई कई एक ही समय में बचत होती है का समर्थन नहीं करता। docs.microsoft.com/en-us/ef/core/saving/async इसके अलावा, वहाँ वास्तव में इन inync विधियों का उपयोग करने पर एक बड़ा फायदा है। उदाहरण के लिए, आप अपने वेबएपी में अन्य अनुरोध प्राप्त कर सकते हैं, जब डेटा सहेजते हैं या बहुत सारे काम करते हैं, या जब आप डेस्कटॉप एप्लिकेशन में होते हैं तो इंटरफ़ेस को खाली न करने के लिए उपयोगकर्ता अनुभव में सुधार करते हैं।
तर्जिया

1

मेरी शेष व्याख्या निम्नलिखित कोड स्निपेट पर आधारित होगी।

using System;
using System.Threading;
using System.Threading.Tasks;
using static System.Console;

public static class Program
{
    const int N = 20;
    static readonly object obj = new object();
    static int counter;

    public static void Job(ConsoleColor color, int multiplier = 1)
    {
        for (long i = 0; i < N * multiplier; i++)
        {
            lock (obj)
            {
                counter++;
                ForegroundColor = color;
                Write($"{Thread.CurrentThread.ManagedThreadId}");
                if (counter % N == 0) WriteLine();
                ResetColor();
            }
            Thread.Sleep(N);
        }
    }

    static async Task JobAsync()
    {
       // intentionally removed
    }

    public static async Task Main()
    {
       // intentionally removed
    }
}

मामला एक

static async Task JobAsync()
{
    Task t = Task.Run(() => Job(ConsoleColor.Red, 1));
    Job(ConsoleColor.Green, 2);
    await t;
    Job(ConsoleColor.Blue, 1);
}

public static async Task Main()
{
    Task t = JobAsync();
    Job(ConsoleColor.White, 1);
    await t;
}

यहाँ छवि विवरण दर्ज करें

टिप्पणी: JobAsyncकार्य t(लाल) की तुलना में लंबे समय तक घूमने वाले समकालिक भाग (हरा) के रूप में तब कार्य tपहले से ही पूरा होता है await t। नतीजतन, निरंतरता (नीला) हरे रंग के समान धागे पर चलती है। Main(सफ़ेद) का समकालिक हिस्सा हरे रंग के समाप्त होने के बाद घूमेगा। इसीलिए एसिंक्रोनस विधि में सिंक्रोनस हिस्सा समस्याग्रस्त है।

केस 2

static async Task JobAsync()
{
    Task t = Task.Run(() => Job(ConsoleColor.Red, 2));
    Job(ConsoleColor.Green, 1);
    await t;
    Job(ConsoleColor.Blue, 1);
}

public static async Task Main()
{
    Task t = JobAsync();
    Job(ConsoleColor.White, 1);
    await t;
}

यहाँ छवि विवरण दर्ज करें

टिप्पणी: यह मामला पहले मामले के विपरीत है। JobAsyncटास्क t(लाल) की तुलना में कम स्पिन का समकालिक भाग (हरा) तब कार्य tपूर्ण नहीं हुआ है await t। नतीजतन, निरंतरता (नीला) हरे रंग के रूप में अलग-अलग धागे पर चलती है। Main(सफ़ेद) का समकालिक हिस्सा हरे रंग की कताई के बाद भी घूमता है।

केस 3

static async Task JobAsync()
{
    Task t = Task.Run(() => Job(ConsoleColor.Red, 1));
    await t;
    Job(ConsoleColor.Green, 1);
    Job(ConsoleColor.Blue, 1);
}

public static async Task Main()
{
    Task t = JobAsync();
    Job(ConsoleColor.White, 1);
    await t;
}

यहाँ छवि विवरण दर्ज करें

टिप्पणी: यह मामला पिछले मामलों में एसिंक्रोनस विधि में सिंक्रोनस भाग के बारे में समस्या को हल करेगा। कार्य tतुरंत प्रतीक्षित है। नतीजतन, निरंतरता (नीला) हरे रंग के रूप में अलग-अलग धागे पर चलती है। Main(सफ़ेद) का समकालिक हिस्सा तुरंत समानांतर होगा JobAsync

यदि आप अन्य मामलों को जोड़ना चाहते हैं, तो बेझिझक संपादित करें।


1

यह कथन गलत है:

सर्वर साइड पर, जब हम Async विधियों का उपयोग करते हैं, तो हमें प्रतीक्षा करने की आवश्यकता होती है।

आपको "प्रतीक्षा" जोड़ने की आवश्यकता नहीं है, awaitकेवल C # में एक सुविधाजनक कीवर्ड है जो आपको कॉल के बाद कोड की अधिक पंक्तियां लिखने में सक्षम बनाता है, और उन अन्य पंक्तियों को केवल सहेजें ऑपरेशन पूरा होने के बाद निष्पादित किया जाएगा। लेकिन जैसा कि आपने बताया, आप SaveChangesइसके बजाय बस कॉल करके पूरा कर सकते हैं SaveChangesAsync

लेकिन मूल रूप से, एक async कॉल इससे बहुत अधिक है। यहां विचार यह है कि यदि कोई अन्य कार्य आप (सर्वर पर) कर सकते हैं जबकि सेव ऑपरेशन प्रगति पर है, तो आपको उपयोग करना चाहिए SaveChangesAsync। "प्रतीक्षा" का उपयोग न करें। बस कॉल करें SaveChangesAsync, और फिर समानांतर में अन्य सामान करना जारी रखें। इसमें वेब ऐप में संभावित रूप से शामिल है, जो सेव पूरा होने से पहले ही क्लाइंट को एक प्रतिक्रिया लौटा देता है। लेकिन निश्चित रूप से, आप अभी भी सहेजें के अंतिम परिणाम की जांच करना चाहेंगे ताकि यदि यह विफल हो जाए, तो आप इसे अपने उपयोगकर्ता से संवाद कर सकते हैं, या इसे किसी भी तरह लॉग इन कर सकते हैं।


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

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

1
यह ज्ञान का एक बहुत उपयोगी टुकड़ा है जिसे मैंने प्रलेखन में पढ़ा था, लेकिन वास्तव में विचार नहीं किया गया था। तो, आपके पास विकल्प है: 1. SaveChangesAsync () से "आग और भूल", जैसे जॉन मेलविल कहते हैं ... जो कुछ मामलों में मेरे लिए उपयोगी है। 2. SaveChangesAsync () से "फायर करने के लिए, कॉल करने वाले पर वापस लौटें, और फिर सेव पूरा होने के बाद कुछ 'पोस्ट-सेव' कोड निष्पादित करें। बहुत उपयोगी टुकड़ा। धन्यवाद।
Parrhesia जो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.