c# - 'Async` और' प्रतीक्षा 'का उपयोग कैसे करें और कब करें




.net asynchronous (12)

मेरी समझ से मुख्य चीजों में से एक जो async और await करने के लिए कोड लिखना और पढ़ना आसान है - लेकिन लंबे समय तक तर्क करने के लिए उन्हें पृष्ठभूमि पृष्ठभूमि धागे के बराबर उपयोग कर रहा है?

मैं वर्तमान में सबसे बुनियादी उदाहरण की कोशिश कर रहा हूं। मैंने कुछ टिप्पणियां इनलाइन जोड़ दी हैं। क्या आप इसे मेरे लिए स्पष्ट कर सकते हैं?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

मेरी समझ से मुख्य चीजों में से एक जो async और प्रतीक्षा करने के लिए कोड लिखना और पढ़ना आसान है।

वे एसिंक्रोनस कोड लिखना और पढ़ना आसान है, हां।

क्या यह वही बात है जो लंबे समय तक तर्क तर्क करने के लिए पृष्ठभूमि धागे को चमकती है?

हर्गिज नहीं।

// मैं यह नहीं समझता कि इस विधि को "async" के रूप में चिह्नित क्यों किया जाना चाहिए।

async कीवर्ड await कीवर्ड सक्षम बनाता है। तो await का उपयोग कर कोई भी विधि async चिह्नित किया जाना चाहिए।

// यह लाइन DoSomethingAsync () विधि से 5 सेकंड की नींद के बाद पहुंच जाती है। इसे तुरंत नहीं पहुंचाया जाना चाहिए?

नहीं, क्योंकि डिफ़ॉल्ट रूप से async विधियां किसी अन्य थ्रेड पर नहीं चलती हैं।

// क्या यह पृष्ठभूमि धागे पर निष्पादित है?

नहीं।

आप मेरी async / await परिचय सहायक पा सकते हैं। आधिकारिक एमएसडीएन दस्तावेज़ भी असामान्य रूप से अच्छे (विशेष रूप से TAP अनुभाग) हैं, और async टीम ने एक उत्कृष्ट FAQ


लंबी अवधि तर्क करने के लिए उन्हें पृष्ठभूमि पृष्ठभूमि धागे के बराबर उपयोग कर रहा है?

यह आलेख एमडीएसएन: असिंक्रोनस प्रोग्रामिंग एसिंक और प्रतीक्षा (सी #) के साथ स्पष्ट रूप से बताता है:

Async और प्रतीक्षा कीवर्ड अतिरिक्त धागे बनाने के लिए कारण नहीं है। Async विधियों को multithreading की आवश्यकता नहीं है क्योंकि एक async विधि अपने धागे पर नहीं चलती है। विधि वर्तमान सिंक्रनाइज़ेशन संदर्भ पर चलती है और विधि सक्रिय होने पर केवल थ्रेड पर समय का उपयोग करती है।


अन्य उत्तरों के आगे, प्रतीक्षा करें (सी # संदर्भ)

और अधिक विशेष रूप से उदाहरण में शामिल है, यह आपकी स्थिति को थोड़ा सा बताता है

निम्न विंडोज प्रपत्र उदाहरण एक async विधि, WaitAsynchronouslyAsync में प्रतीक्षा के उपयोग को दर्शाता है। WaitSynchronously के व्यवहार के साथ उस विधि के व्यवहार की तुलना करें। किसी काम पर लागू एक प्रतीक्षा ऑपरेटर के बिना, प्रतीक्षा परिभाषा में एसिंक संशोधक के उपयोग के बावजूद प्रतीक्षा करें और थ्रेड पर कॉल करें। उसके शरीर में सो जाओ।

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

इस उत्तर का उद्देश्य एएसपी.नेट को विशिष्ट जानकारी प्रदान करना है।

एमवीसी नियंत्रक में async / प्रतीक्षा का उपयोग करके, थ्रेड पूल उपयोग को बढ़ाने और एक बेहतर थ्रूपुट प्राप्त करना संभव है, जैसा कि नीचे दिए गए लेख में बताया गया है,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

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


एक सरल कंसोल एप्लिकेशन चलाने के लिए यह बेवकूफ https://dotnetfiddle.net/VhZdLU (और यदि संभव हो तो इसे बेहतर करें) देखें, जो कार्य, कार्य का उपयोग दिखाता है .aitएल (), async और उसी प्रोग्राम में ऑपरेटरों का इंतजार करता है

इस पहेली को आपके निष्पादन चक्र अवधारणा को साफ़ करना चाहिए।

नमूना कोड यहाँ है

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

आउटपुट विंडो से आ रहा ट्रेस:


एक सरल कंसोल कार्यक्रम में कार्रवाई में उपर्युक्त स्पष्टीकरण दिखा रहा है -

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

और आउटपुट है:

Starting Long Running method...
Press any key to exit...
End Long Running method...

इस प्रकार,

  1. मुख्य TestAsyncAwaitMethods के माध्यम से लंबी चलती विधि शुरू करता है। वह तुरंत वर्तमान धागे को रोक दिए बिना लौटता है और हम तुरंत 'बाहर निकलने के लिए कोई भी कुंजी दबाएं' संदेश देखते हैं
  2. यह सब कुछ, LongRunningMethod पृष्ठभूमि में चल रहा है। एक बार यह पूरा होने के बाद, थ्रेडपूल से एक और धागा इस संदर्भ को उठाता है और अंतिम संदेश प्रदर्शित करता है

इस प्रकार, धागा अवरुद्ध नहीं है।


निम्नलिखित कोड में, HttpClient विधि GetByteArrayAsync एक कार्य देता है, getContentsTask। यह कार्य पूरा होने पर वास्तविक बाइट सरणी का उत्पादन करने का वादा है। GetPontentsTask को पूरा होने तक SumPageSizesAsync में निष्पादन को निलंबित करने के लिए getContentsTask प्राप्त करने के लिए प्रतीक्षा ऑपरेटर लागू किया जाता है। इस बीच, SumPageSizesAsync के कॉलर पर नियंत्रण वापस कर दिया जाता है। जब GetContentsTask समाप्त हो जाता है, तो प्रतीक्षा अभिव्यक्ति एक बाइट सरणी का मूल्यांकन करती है।

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

नीचे कोड है जो संवाद खोलकर एक्सेल फ़ाइल पढ़ता है और फिर एसिंक का उपयोग करता है और कोड को एसिंक्रोनस चलाने के लिए प्रतीक्षा करता है जो एक से एक पंक्ति को एक्सेल से जोड़ता है और ग्रिड से बांधता है

    namespace EmailBillingRates
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        lblProcessing.Text = "";
    }

    private async void btnReadExcel_Click(object sender, EventArgs e)
    {
        string filename = OpenFileDialog();

        Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
        Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
        Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
        Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
        try
        {
            Task<int> longRunningTask = BindGrid(xlRange);
            int result = await longRunningTask;

        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message.ToString());
        }
        finally
        {
            //cleanup  
           // GC.Collect();
            //GC.WaitForPendingFinalizers();

            //rule of thumb for releasing com objects:  
            //  never use two dots, all COM objects must be referenced and released individually  
            //  ex: [somthing].[something].[something] is bad  

            //release com objects to fully kill excel process from running in the background  
            Marshal.ReleaseComObject(xlRange);
            Marshal.ReleaseComObject(xlWorksheet);

            //close and release  
            xlWorkbook.Close();
            Marshal.ReleaseComObject(xlWorkbook);

            //quit and release  
            xlApp.Quit();
            Marshal.ReleaseComObject(xlApp);
        }

    }

    private void btnSendEmail_Click(object sender, EventArgs e)
    {

    }

    private string OpenFileDialog()
    {
        string filename = "";
        OpenFileDialog fdlg = new OpenFileDialog();
        fdlg.Title = "Excel File Dialog";
        fdlg.InitialDirectory = @"c:\";
        fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
        fdlg.FilterIndex = 2;
        fdlg.RestoreDirectory = true;
        if (fdlg.ShowDialog() == DialogResult.OK)
        {
            filename = fdlg.FileName;
        }
        return filename;
    }

    private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
    {
        lblProcessing.Text = "Processing File.. Please wait";
        int rowCount = xlRange.Rows.Count;
        int colCount = xlRange.Columns.Count;

        // dt.Column = colCount;  
        dataGridView1.ColumnCount = colCount;
        dataGridView1.RowCount = rowCount;

        for (int i = 1; i <= rowCount; i++)
        {
            for (int j = 1; j <= colCount; j++)
            {
                //write the value to the Grid  
                if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                {
                     await Task.Delay(1);
                     dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                }

            }
        }
        lblProcessing.Text = "";
        return 0;
    }
}

internal class async
{
}

} `


यहां आने वाले लोगों को स्पष्ट करने के लिए यहां एक त्वरित कंसोल प्रोग्राम है। "टास्कटो" विधि आपकी लंबी चल रही विधि है जिसे आप एसिंक बनाना चाहते हैं। इसे एसिंक को टेस्टएसिंक विधि द्वारा किया जाता है। टेस्ट लूप विधि सिर्फ "टास्क टोडो" कार्यों के माध्यम से चलती है और उन्हें Async चलाती है। आप परिणामों में देख सकते हैं क्योंकि वे रन-रन से उसी क्रम में पूर्ण नहीं होते हैं - वे पूरा होने पर कंसोल UI थ्रेड पर रिपोर्ट कर रहे हैं। सरल, लेकिन मुझे लगता है कि सरल उदाहरण अधिक शामिल उदाहरणों से बेहतर पैटर्न के मूल को बाहर लाते हैं:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

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

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

आप निश्चित रूप से विभिन्न साधनों का उपयोग करके पृष्ठभूमि धागे का इंतजार कर सकते हैं:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

इन टिप्पणियों के लिए पूरा कोड https://github.com/marcusts/xamarin-forms-annoyances । AwaitAsyncAntipattern.sln नामक समाधान देखें।

गिटहब साइट इस विषय पर अधिक विस्तृत चर्चा के लिंक भी प्रदान करती है।


async का उपयोग करते समय और कंपाइलर का await पृष्ठभूमि में एक राज्य मशीन उत्पन्न करता है।

यहां एक उदाहरण दिया गया है जिस पर मुझे आशा है कि मैं कुछ उच्च स्तरीय विवरणों को समझा सकता हूं जो चल रहे हैं:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

ठीक है, तो यहां क्या होता है:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); LongRunningOperation निष्पादित करना शुरू होता है

  2. आइए मुख्य थ्रेड (थ्रेड आईडी = 1) मानने पर स्वतंत्र काम किया जाता है, फिर await longRunningTask समय तक await longRunningTask वाला await longRunningTask पहुंच गया है।

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

एक दूसरा मामला यह होगा कि longRunningTask पहले ही अपना निष्पादन समाप्त कर चुका है और परिणाम उपलब्ध है। await longRunningTask समय तक await longRunningTask तक पहुंचने पर हमारे पास पहले से ही परिणाम होता है ताकि कोड एक ही थ्रेड पर निष्पादन जारी रहेगा। (इस मामले में प्रिंटिंग परिणाम कंसोल के लिए)। बेशक यह उपर्युक्त उदाहरण का मामला नहीं है, जहां एक Task.Delay(1000)Task.Delay(1000) शामिल है।


public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}






async-await