'वेट' काम करता है, लेकिन कॉलिंग टास्क। रिजल्ट हैंग / गतिरोध


126

जब मैं इसे चलाता हूं तो मेरे पास निम्नलिखित चार परीक्षण होते हैं और अंतिम लटका रहता है। ऐसा क्यों होता है:

[Test]
public void CheckOnceResultTest()
{
    Assert.IsTrue(CheckStatus().Result);
}

[Test]
public async void CheckOnceAwaitTest()
{
    Assert.IsTrue(await CheckStatus());
}

[Test]
public async void CheckStatusTwiceAwaitTest()
{
    Assert.IsTrue(await CheckStatus());
    Assert.IsTrue(await CheckStatus());
}

[Test]
public async void CheckStatusTwiceResultTest()
{
    Assert.IsTrue(CheckStatus().Result); // This hangs
    Assert.IsTrue(await CheckStatus());
}

private async Task<bool> CheckStatus()
{
    var restClient = new RestClient(@"https://api.test.nordnet.se/next/1");
    Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
    IRestResponse<DummyServiceStatus> response = await restResponse;
    return response.Data.SystemRunning;
}

मैं इस एक्सटेंशन विधि का उपयोग restsharp RestClient के लिए करता हूं :

public static class RestClientExt
{
    public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
    {
        var tcs = new TaskCompletionSource<IRestResponse<T>>();
        RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
        return tcs.Task;
    }
}
public class DummyServiceStatus
{
    public string Message { get; set; }
    public bool ValidVersion { get; set; }
    public bool SystemRunning { get; set; }
    public bool SkipPhrase { get; set; }
    public long Timestamp { get; set; }
}

अंतिम परीक्षण क्यों लटका हुआ है?


7
आपको async विधियों से शून्य लौटने से बचना चाहिए। यह केवल एग्ज़ाइटिंग ईवेंट हैंडलर्स के साथ पिछड़े संगतता के लिए है, ज्यादातर इंटरफ़ेस कोड में। यदि आपका async विधि कुछ भी वापस नहीं करता है, तो उसे टास्क वापस करना चाहिए। मुझे MSTest और शून्य के साथ async परीक्षण करने में कई समस्याएँ थीं।
घोर

2
@ghord: MSTest async voidइकाई परीक्षण विधियों का बिल्कुल समर्थन नहीं करता है ; वे बस काम नहीं करेगा। हालाँकि, NUnit करता है। यही कारण है कि कहा, मैं पसंद करते हैं के सामान्य सिद्धांत से सहमत async Taskअधिक async void
स्टीफन क्लीयर

@StephenCleary हाँ, हालांकि इसे VS2012 के दांव में अनुमति दी गई थी, जो सभी प्रकार के मुद्दों का कारण बन रहा था।
घोर

जवाबों:


88

आप मानक गतिरोध की स्थिति में चल रहे हैं जिसका वर्णन मैं अपने ब्लॉग पर और एक MSDN लेख में करता हूं : यह asyncविधि एक थ्रेड पर इसकी निरंतरता को शेड्यूल करने का प्रयास कर रही है जिसे कॉल द्वारा अवरुद्ध किया जा रहा है Result

इस मामले में, आपका परीक्षण विधियों SynchronizationContextको निष्पादित करने के लिए NUnit द्वारा उपयोग किया जाने वाला एक है async void। मैं async Taskइसके बजाय परीक्षण विधियों का उपयोग करने की कोशिश करूंगा ।


4
बदलकर async टास्क ने काम किया, अब मुझे आपके लिंक की सामग्री को एक-दो बार पढ़ना है, ty sir।
जोहान लार्सन

@MarioLopez: समाधान " asyncसभी तरह से" का उपयोग करना है (जैसा कि मेरे MSDN लेख में उल्लेख किया गया है)। दूसरे शब्दों में - मेरे ब्लॉग पोस्ट के शीर्षक के अनुसार - "async कोड को ब्लॉक न करें"।
स्टीफन क्लीयर

1
@StephenCleary क्या होगा अगर मुझे एक कंस्ट्रक्टर के अंदर एक async विधि को कॉल करना है? कंस्ट्रक्टर्स एसिंक्स नहीं हो सकते।
रायकोल अमरो

1
@StephenCleary एसओ और आपके लेखों में आपके लगभग सभी उत्तरों में, मैं कभी भी आपके बारे में बात करता हूं जो आपको Wait()कॉलिंग विधि बनाने के साथ बदल रहा है async। लेकिन मेरे लिए, यह समस्या को ऊपर की ओर धकेलता हुआ प्रतीत होता है। कुछ बिंदु पर, कुछ को सिंक्रोनाइज़ करना पड़ता है। क्या होगा अगर मेरा फ़ंक्शन उद्देश्यपूर्ण रूप से सिंक्रोनस है क्योंकि यह लंबे समय तक चलने वाले कार्यकर्ता थ्रेड्स का प्रबंधन करता है Task.Run()? मैं अपने NUnit परीक्षण के अंदर गतिरोध के बिना समाप्त करने के लिए कैसे प्रतीक्षा करूं?
void.pointer

1
@ void.pointer: At some point, something has to be managed synchronously.- बिलकुल नहीं। यूआई ऐप्स के लिए, एंट्रीपॉइंट एक async voidइवेंट हैंडलर हो सकता है । सर्वर ऐप्स के लिए, प्रवेश बिंदु एक async Task<T>कार्रवाई हो सकती है। asyncअवरुद्ध धागे से बचने के लिए दोनों का उपयोग करना बेहतर है । आप अपने NUnit परीक्षण को सिंक्रोनस या एसिंक्स हो सकते हैं; यदि async, के async Taskबजाय इसे बनाते हैं async void। यदि यह समकालिक है, SynchronizationContextतो इसमें गतिरोध नहीं होना चाहिए।
स्टीफन क्लीयर

222

एक async विधि के माध्यम से एक मूल्य प्राप्त करना:

var result = Task.Run(() => asyncGetValue()).Result;

एसिंक्रोनस तरीके से कॉल करना

Task.Run( () => asyncMethod()).Wait();

टास्क के उपयोग के कारण कोई गतिरोध समस्या नहीं होगी।


15
-1 async voidयूनिट परीक्षण विधियों के उपयोग को प्रोत्साहित करने और SynchronizationContextपरीक्षण के तहत प्रणाली से प्रदान की गई समान-थ्रेड गारंटी को हटाने के लिए।
स्टीफन क्लीयर

68
@StephenCleary: Async शून्य का कोई "उत्साहवर्धक" नहीं है। यह केवल डेडलॉक मुद्दे को हल करने के लिए वैध सी # निर्माण को नियोजित कर रहा है। उपरोक्त स्निपेट ओपी के मुद्दे पर एक अनिवार्य और सरल कार्य है। Stackoverflow समस्याओं के समाधान के बारे में है, न कि आत्म-प्रचार।
हरमन स्कोनफेल्ड

81
@StephenCleary: आपके लेख वास्तव में समाधान को स्पष्ट रूप से व्यक्त नहीं करते (कम से कम स्पष्ट रूप से नहीं) और यहां तक ​​कि अगर आपके पास कोई समाधान था, तो आप ऐसे निर्माणों का अप्रत्यक्ष रूप से उपयोग करेंगे। मेरा समाधान स्पष्ट रूप से संदर्भों का उपयोग नहीं करता है, तो क्या? मुद्दा यह है, मेरा काम करता है और यह एक-लाइनर है। समस्या को हल करने के लिए दो ब्लॉग पोस्ट और हजारों शब्दों की आवश्यकता नहीं थी। नोट: मैं भी async शून्य का उपयोग नहीं करते हैं , इसलिए मैं वास्तव में नहीं जानता कि आप किस बारे में हैं .. क्या आप अपने संक्षिप्त और उचित उत्तर में कहीं भी "async शून्य" देखते हैं?
हरमन शोनीफेल्ड

15
, @HermanSchoenfeld अगर आप जोड़ा क्यों करने के लिए कैसे , मेरा मानना है कि आपका जवाब एक बहुत लाभ होगा।
ironstone13

19
मुझे पता है कि यह देर से है, लेकिन आपको .GetAwaiter().GetResult()इसके बजाय उपयोग करना चाहिए .Resultताकि कोई भी Exceptionलपेटा न जाए।
कैमिलो टेरेविंटो

15

आप ConfigureAwait(false)इस पंक्ति में गतिरोध जोड़ने से बच सकते हैं :

IRestResponse<DummyServiceStatus> response = await restResponse;

=>

IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);

मैंने अपने ब्लॉग पोस्ट में इस नुकसान का वर्णन किया है async / प्रतीक्षा के नुकसान


9

आप Task.Result संपत्ति का उपयोग करके UI को अवरुद्ध कर रहे हैं। में MSDN प्रलेखन वे स्पष्ट रूप से उल्लेख किया है कि,

" परिणाम संपत्ति एक अवरुद्ध संपत्ति है। यदि आप इसके कार्य समाप्त होने से पहले इसे एक्सेस करने का प्रयास करते हैं, तो जो धागा वर्तमान में सक्रिय है वह कार्य पूरा होने तक अवरुद्ध है और मूल्य उपलब्ध है। ज्यादातर मामलों में, आपको Await का उपयोग करके मूल्य का उपयोग करना चाहिए। या सीधे संपत्ति तक पहुंचने के बजाय प्रतीक्षा करें । "

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


3

यदि आपको कोई कॉलबैक नहीं मिलता है या कंट्रोल हैंग हो जाता है, तो सेवा / एपीआई एसिंक्स्ट फ़ंक्शन को कॉल करने के बाद, आपको उसी संदर्भ में परिणाम देने के लिए Context को कॉन्फ़िगर करना होगा।

उपयोग TestAsync().ConfigureAwait(continueOnCapturedContext: false);

आप केवल वेब अनुप्रयोगों में इस समस्या का सामना कर रहे होंगे, लेकिन अंदर नहीं static void main


ConfigureAwaitमूल थ्रेड संदर्भ में नहीं चलने से कुछ परिदृश्यों में गतिरोध से बचा जाता है।
डैविडक्रिल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.