मेरे पास 3 फ़ाइलें हैं, प्रत्येक 1 मिलियन पंक्तियाँ लंबी हैं और मैं उन्हें पंक्ति दर पंक्ति पढ़ रहा हूँ। कोई प्रसंस्करण नहीं, बस पढ़ रहा हूं क्योंकि मैं चीजों का परीक्षण कर रहा हूं।

अगर मैं इसे सिंक्रोनाइज़ करता हूं तो इसमें 1 सेकंड का समय लगता है। यदि मैं प्रत्येक फ़ाइल के लिए एक थ्रेड का उपयोग करने के लिए स्विच करता हूं, तो यह थोड़ा तेज़ है (कोड नीचे नहीं है, लेकिन मैंने बस एक नया थ्रेड बनाया है और इसे प्रत्येक फ़ाइल के लिए शुरू किया है)।

जब मैं async में बदलता हूं, तो 40 सेकंड में 40 गुना लंबा समय लगता है। अगर मैं वास्तविक प्रसंस्करण करने के लिए किसी भी काम में जोड़ता हूं, तो मैं यह नहीं देख सकता कि मैं कभी भी सिंक्रोनस पर एसिंक का उपयोग कैसे करूं या यदि मैं थ्रेड का उपयोग करके एक उत्तरदायी एप्लिकेशन चाहता हूं।

या क्या मैं इस कोड के साथ मौलिक रूप से कुछ गलत कर रहा हूं और जैसा कि एसिंक्स का इरादा नहीं था?

धन्यवाद।

class AsyncTestIOBound
{
    Stopwatch sw = new Stopwatch();
    internal void Tests()
    {
        DoSynchronous();
        DoASynchronous();
    }
    #region sync
    private void DoSynchronous()
    {
        sw.Restart();
        var start = sw.ElapsedMilliseconds;
        Console.WriteLine($"Starting Sync Test");
        DoSync("Addresses", "SampleLargeFile1.txt");
        DoSync("routes   ", "SampleLargeFile2.txt");
        DoSync("Equipment", "SampleLargeFile3.txt");
        sw.Stop();
        Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
        Console.ReadKey();
    }

    private long DoSync(string v, string filename)
    {
        string line;
        long counter = 0;
        using (StreamReader file = new StreamReader(filename))
        {
            while ((line = file.ReadLine()) != null)
            {
                counter++;
            }
        }
        Console.WriteLine($"{v}: T{Thread.CurrentThread.ManagedThreadId}: Lines: {counter}");
        return counter;
    }
    #endregion

    #region async
    private void DoASynchronous()
    {
        sw.Restart();
        var start = sw.ElapsedMilliseconds;
        Console.WriteLine($"Starting Sync Test");
        Task a=DoASync("Addresses", "SampleLargeFile1.txt");
        Task b=DoASync("routes   ", "SampleLargeFile2.txt");
        Task c=DoASync("Equipment", "SampleLargeFile3.txt");
        Task.WaitAll(a, b, c);
        sw.Stop();
        Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
        Console.ReadKey();
    }

    private async Task<long> DoASync(string v, string filename)
    {
        string line;
        long counter = 0;
        using (StreamReader file = new StreamReader(filename))
        {
            while ((line = await file.ReadLineAsync()) != null)
            {
                counter++;
            }
        }
        Console.WriteLine($"{v}: T{Thread.CurrentThread.ManagedThreadId}: Lines: {counter}");
        return counter;
    }
    #endregion

}
1
Neil Walker 18 फरवरी 2019, 21:29

2 जवाब

सबसे बढ़िया उत्तर

चूंकि आप एक विशाल लूप में कई बार await का उपयोग कर रहे हैं (आपके मामले में, "SampleLargeFile" की प्रत्येक पंक्ति के माध्यम से लूपिंग), आप बहुत सारे संदर्भ स्विचिंग कर रहे हैं, और ओवरहेड वास्तव में खराब हो सकता है।

प्रत्येक पंक्ति के लिए, आपका कोड शायद प्रत्येक फ़ाइल के बीच स्विच कर रहा है। यदि आपका कंप्यूटर हार्ड ड्राइव का उपयोग करता है, तो यह और भी खराब हो सकता है। कल्पना कीजिए कि आपके एचडी का सिर पागल हो रहा है।

जब आप सामान्य धागे का उपयोग करते हैं, तो आप प्रत्येक पंक्ति के संदर्भ को स्विच नहीं कर रहे हैं।

इसे हल करने के लिए, फ़ाइल को केवल एक बार में पढ़ें। आप अभी भी async/await (ReadToEndAsync()) का उपयोग कर सकते हैं और एक अच्छा प्रदर्शन प्राप्त कर सकते हैं।

संपादित करें

तो, आप async का उपयोग करके टेक्स्ट फ़ाइल पर लाइनों को गिनने का प्रयास कर रहे हैं, है ना?

इसे आज़माएं (पूरी फ़ाइल को मेमोरी में लोड करने की आवश्यकता नहीं है):

private async Task<int> CountLines(string path)
{
    int count = 0;
    await Task.Run(() =>
    {
        using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        using (BufferedStream bs = new BufferedStream(fs))
        using (StreamReader sr = new StreamReader(bs))
        {
            while (sr.ReadLine() != null)
            {
                count++;
            }
        }
    });
    return count;
}
3
Guilherme 18 फरवरी 2019, 21:51

कुछ चीजें। पहले मैं सभी पंक्तियों को एक बार में async विधि में पढ़ूंगा ताकि आप केवल एक बार (प्रति पंक्ति के बजाय) प्रतीक्षा कर रहे हों।

private async Task<long> DoASync(string v, string filename)
{
    string lines;
    long counter = 0;
    using (StreamReader file = new StreamReader(filename))
    {
        lines = await reader.ReadToEndAsync();
    }
    Console.WriteLine($"{v}: T{Thread.CurrentThread.ManagedThreadId}: Lines: {lines.Split('\n').Length}");
    return counter;
}

इसके बाद, आप प्रत्येक कार्य के लिए व्यक्तिगत रूप से प्रतीक्षा भी कर सकते हैं। यह आपके सीपीयू को संभवतः 3 के बीच स्विच करने के बजाय एक समय में केवल एक पर ध्यान केंद्रित करने का कारण बनेगा, जिससे अधिक ओवरहेड होगा।

private async void DoASynchronous()
{
    sw.Restart();
    var start = sw.ElapsedMilliseconds;
    Console.WriteLine($"Starting Sync Test");
    await DoASync("Addresses", "SampleLargeFile1.txt");
    await DoASync("routes   ", "SampleLargeFile2.txt");
    await DoASync("Equipment", "SampleLargeFile3.txt");
    sw.Stop();
    Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
    Console.ReadKey();
}

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

अधिक जानकारी, देखें: क्या async वेटिंग प्रसंग स्विचिंग को बढ़ाता है

2
d.moncada 18 फरवरी 2019, 21:51