मैंने एक कस्टम TaskScheduler लिखा है जो एक ही थ्रेड पर दिए गए कार्यों को निष्पादित करने वाला है। इस कार्य शेड्यूलर का उपयोग कस्टम कार्य फ़ैक्टरी के साथ किया जाता है। यह कार्य कारखाना एक एसिंक विधि ReadFileAsync निष्पादित करता है जो StreamReader की एक अन्य एसिंक विधि ReadToEndAsync कहता है।

मैंने देखा है कि ReadToEndAsync().ConfigureAwait(false) का उपयोग करने के बाद, वर्तमान कार्य अनुसूचक को वापस डिफ़ॉल्ट एक, ThreadPoolTaskScheduler पर वापस कर दिया जाता है। अगर मैं ConfigureAwait(false) को हटा देता हूं, तो कस्टम टास्क शेड्यूलर SameThreadTaskScheduler रखा जाता है। क्यों? क्या इसके निष्पादन के बाद उसी कस्टम शेड्यूलर के साथ ConfigureAwait(false) का उपयोग करने का कोई तरीका है?

मैंने कई चीजों की कोशिश की है, लेकिन नतीजा वही है:

  • कस्टम TaskFactory के एनम झंडे बदलें
  • एक कस्टम सिंक्रोनाइज़ेशन संदर्भ का उपयोग करना जो Posts कॉलबैक को थ्रेड पूल के बजाय सिंक्रोनाइज़ करता है
  • कार्य फ़ैक्टरी पर निष्पादित कार्य फ़ंक्शन के अंदर सिंक्रनाइज़ेशन को बदलें और वापस लाएं
  • कार्य फ़ैक्टरी पर निष्पादित कार्य फ़ंक्शन के बाहर सिंक्रनाइज़ेशन को बदलें और वापस लाएं
public static class Program
{
    private static readonly string DesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

    public static void Main()
    {
        _ = AsyncHelper.RunSynchronously(ReadFileAsync);
    }

    private static async Task<string> ReadFileAsync()
    {
        // Prints "SameThreadTaskScheduler"
        Console.WriteLine(TaskScheduler.Current.GetType().Name);

        using var fs = File.OpenText(Path.Combine(DesktopPath, "hello.txt"));
        var content = await fs.ReadToEndAsync().ConfigureAwait(false); // <-------- HERE

        // With ReadToEndAsync().ConfigureAwait(false), prints "ThreadPoolTaskScheduler"
        // With ReadToEndAsync() only, prints "SameThreadTaskScheduler"
        Console.WriteLine(TaskScheduler.Current.GetType().Name);

        return content;
    }
}

public static class AsyncHelper
{
    private static readonly TaskFactory SameThreadTaskFactory = new TaskFactory(
        CancellationToken.None,
        TaskCreationOptions.None,
        TaskContinuationOptions.None,
        new SameThreadTaskScheduler());

    public static TResult RunSynchronously<TResult>(Func<Task<TResult>> func)
    {
        var oldContext = SynchronizationContext.Current;

        try
        {
            SynchronizationContext.SetSynchronizationContext(null);
            return SameThreadTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(oldContext);
        }
    }
}

public sealed class SameThreadTaskScheduler : TaskScheduler
{
    public override int MaximumConcurrencyLevel => 1;

    protected override void QueueTask(Task task)
    {
        this.TryExecuteTask(task);
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        this.TryExecuteTask(task);
        return true;
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return Enumerable.Empty<Task>();
    }
}
0
Anthony Simmon 21 जिंदा 2021, 18:46
1
यदि आप कस्टम स्थिति को एकसमान बनाए रखना चाहते हैं, तो यह जानने में बहुत मदद मिलेगी कि आप ConfigureAwait(false) को क्यों कॉल कर रहे हैं।
 – 
Damien_The_Unbeliever
21 जिंदा 2021, 19:00
1
ऐसा लगता है कि आप निराश हैं क्योंकि ConfigureAwait(false) वही करता है जो करना चाहिए। जो थोड़े अजीब है। यह पूछने जैसा है कि + ऑपरेटर अतिरिक्त क्यों करता है, और आप इसे जोड़ने से कैसे रोक सकते हैं। अच्छी तरह से ठीक है। यदि आपको यह पसंद नहीं है कि ConfigureAwait(false) await को कॉन्फ़िगर करता है ताकि यह वर्तमान संदर्भ या शेड्यूलर को कैप्चर न करे, तो आप इसे क्या करना चाहेंगे? क्या आप इसे नो-ऑप बनाना चाहेंगे?
 – 
Theodor Zoulias
21 जिंदा 2021, 21:51
आप अपने कस्टम शेड्यूलर के साथ क्या हासिल करने की कोशिश कर रहे हैं? क्या इसके बजाय AsyncLocal<T> से इसे पूरा किया जा सकता है? क्या आप एसिंक्रोनस कोड को ब्लॉक कर रहे हैं?
 – 
Stephen Cleary
22 जिंदा 2021, 02:37
मैं यह समझने की कोशिश कर रहा था कि ConfigureAwait(false) के साथ async को कॉल करने के बाद वर्तमान कस्टम कार्य शेड्यूलर क्यों बदलता है। आपकी टिप्पणियों और उत्तरों के साथ, अब मैं समझ गया!
 – 
Anthony Simmon
22 जिंदा 2021, 08:31
मेरी कहानी यह है कि मैं एक डेस्कटॉप .NET 4.5 एप्लिकेशन पर काम कर रहा हूं जो एक कस्टम टास्क शेड्यूलर का उपयोग करता है ताकि थ्रेड की संख्या को सीमित किया जा सके। इसे .NET Core 3.1 में माइग्रेट करने का प्रयास करते समय, मुझे HttpWebRequest के उपयोग को HttpClient से बदलना पड़ा। सबसे पहले, मैंने देखा कि यह कस्टम थ्रेड-सीमक कार्य शेड्यूलर का उपयोग कर रहा था, जिसके परिणामस्वरूप थ्रेड भुखमरी हुई। इसलिए मैं यह सुनिश्चित करने का प्रयास कर रहा हूं कि HttpClient async विधियों और कॉलबैक को उसी थ्रेड पर किसी अन्य कार्य शेड्यूलर के साथ निष्पादित किया गया था।
 – 
Anthony Simmon
22 जिंदा 2021, 08:31

1 उत्तर

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

ConfigureAwait(bool continueOnCapturedContext) में पैरामीटर continueOnCapturedContext का निम्नलिखित अर्थ है: यदि true निर्दिष्ट है, तो इसका मतलब है कि निरंतरता को कैप्चर किए गए मूल संदर्भ में वापस मार्शल किया जाना चाहिए। यदि false निर्दिष्ट है, तो निरंतरता एक मनमाना संदर्भ पर चल सकती है।

सिंक्रोनाइज़ेशन संदर्भ शेड्यूलिंग के लिए एक अमूर्त है। एक TaskScheduler एक ठोस कार्यान्वयन है। तो ConfigureAwait(false) निर्दिष्ट करके, आप कहते हैं कि किसी भी कार्य शेड्यूलर का उपयोग किया जा सकता है। यदि आप अपने विशेष कार्य शेड्यूलर का उपयोग करना चाहते हैं, तो ConfigureAwait(true) का उपयोग करें।

इस विषय पर अधिक जानकारी के लिए, इस पोस्ट पर एक नज़र डालें।

2
Xaver 21 जिंदा 2021, 19:03