5

मैं एपीआई को साफ करने के लिए टीपीएल के साथ निम्नलिखित डेटाग्राम सॉकेट ऑपरेशन को लपेटना चाहता हूं ताकि यह async और await के साथ अच्छी तरह से काम करे, StreamSocket वर्ग की तरह।मैं datagramSocket.Message को एसिंक/प्रतीक्षा के साथ उपयोग के लिए कैसे प्राप्त कर सकता हूं?

public static async Task<bool> TestAsync(HostName hostName, string serviceName, byte[] data) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    var socket = new DatagramSocket(); 
    socket.MessageReceived += (sender, e) => 
    { 
     var status = false; // Status value somehow derived from e etc. 
     tcs.SetResult(status); 
    }; 
    await socket.ConnectAsync(hostName, serviceName); 
    var stream = await socket.GetOutputStreamAsync(); 
    var writer = new DataWriter(stream); 
    writer.WriteBytes(data); 
    await writer.StoreAsync(); 
    return tcs.Task; 
} 

चिपका बिंदु MessageReceived घटना है जो घटना अतुल्यकालिक पैटर्न का एक अजीब उलझन और नए async पैटर्न में DatagramSocket वर्ग बदल जाता है। वैसे भी, TaskCompletionSource<T> मुझे बाद वाले के अनुरूप हैंडलर को अनुकूलित करने की अनुमति देता है, इसलिए यह बहुत डरावना नहीं है।

यह तब तक बहुत अच्छा काम करता है जब तक कि एंडपॉइंट कभी भी कोई डेटा न लौटाए। MessageReceived हैंडलर से जुड़ा कार्य कभी पूरा नहीं होता है और इस प्रकार TestAsync से कार्य वापस नहीं आता है।

टाइमआउट और रद्दीकरण को शामिल करने के लिए इस ऑपरेशन को अच्छी तरह से लपेटने का सही तरीका क्या है? मैं इस फ़ंक्शन को बाद में के लिए CancellationToken तर्क लेने के लिए विस्तारित करना चाहता हूं, लेकिन मैं इसके साथ क्या करूँ? केवल एक चीज मैं ले कर आए हैं एक अतिरिक्त "निगरानी" कार्य का उपयोग कर Task.Delay जो मैं एक टाइमआउट मान और रद्द करने टोकन निम्नलिखित लाइनों के साथ इन दोनों व्यवहार का समर्थन करने के पारित करने के लिए बनाने के लिए है:

public static async Task<bool> CancellableTimeoutableTestAsync(HostName hostName, string serviceName, byte[] data, CancellationToken userToken, int timeout) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    var socket = new DatagramSocket(); 
    socket.MessageReceived += (sender, e) => 
    { 
     var status = false; // Status value somehow derived from e etc. 
     tcs.SetResult(status); 
    }; 
    await socket.ConnectAsync(hostName, serviceName); 
    var stream = await socket.GetOutputStreamAsync(); 
    var writer = new DataWriter(stream); 
    writer.WriteBytes(data); 
    await writer.StoreAsync(); 

    var delayTask = Task.Delay(timeout, userToken); 
    var t1 = delayTask.ContinueWith(t => { /* Do something to tcs to indicate timeout */ }, TaskContinuationOptions.OnlyOnRanToCompletion); 
    var t2 = delayTask.ContinueWith(t => { tcs.SetCanceled(); }, TaskContinuationOptions.OnlyOnCanceled); 

    return tcs.Task; 
} 

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

साइड नोट: क्या मैं अकेला व्यक्ति हूं जो DatagramSocket एपीआई सामान्य रूप से उलझन में है? ऐसा लगता है कि यह IAsyncAction विनरेट मॉडल और टीपीएल के कुछ बदसूरत ईएपी के साथ एक बदसूरत समूह प्रतीत नहीं होता है, मैं एक एपीआई के साथ बहुत सहज नहीं हूं जिसका उद्देश्य मूल रूप से कनेक्शन रहित प्रोटोकॉल का प्रतिनिधित्व करना है जैसे यूडीपी ConnectAsync नामक विधियों उन्हें। यह मेरे संदर्भ में एक विरोधाभास प्रतीत होता है।

+0

पूरी दूसरी विधि के बजाय, एक नई विधि बनाएं जो कार्य को पहले कार्य से जोड़ती है। डेले और कार्य का उपयोग करता है। जब भी। एक बार यह लौटने के बाद या तो आपका कार्य पूरा हो गया या टाइमआउट हुआ। –

उत्तर

2

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

और मैं मानता हूँ कि नाम Connect 100% मतलब नहीं है, लेकिन मुझे लगता है कि यह ज्यादातर करता है समझ में आता है, विशेष रूप से इसे और अधिक StreamSocket के अनुरूप बनाने के लिए। और आपको इस तरह की एक विधि की आवश्यकता है, ताकि सिस्टम डोमेन नाम को हल कर सके और पोर्ट को अपनी सॉकेट में असाइन कर सके।

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

यह भी अगर आप इसे ठीक से लागू करने, अक्षम नहीं होगा: क्या आप वाकई Task पूरा कर लिया है के बाद, MessageReceived घटना से समाप्त कर दी है कि, Delay() के साथ जुड़े टाइमर निपटान किया जाता है बनाना चाहिए (आप करते हैं कि टोकन रद्द करके आप Delay() पर पास हुए) और पासपोर्ट CancellationToken के साथ पंजीकृत प्रतिनिधि को अनियंत्रित किया गया है (मुझे लगता है कि आपको इसके लिए Delay() का उपयोग करके सीधे (ab) के Register() का उपयोग करना चाहिए)।

दौड़ की स्थिति के बारे में, निश्चित रूप से आपको उनके बारे में सोचना होगा। लेकिन यहां से निपटने का एक अपेक्षाकृत सरल तरीका है: TaskCompletionSource के तरीकों का उपयोग करें (उदा। TrySetResult())।

0

टाइमआउट: कार्य पूरा करने के लिए टाइमर प्रारंभ करें और tcs.TrySetCancelled() का उपयोग करें। रद्द करने के लिए cancellationToken.Register कॉलबैक पंजीकृत करने के लिए जिसमें आप रद्द भी सेट करते हैं। हमेशा टाइमर का निपटान करने के लिए सावधान रहें।

मेरा सुझाव है कि आप टाइमर तर्क को पुन: प्रयोज्य सहायक विधि में ले जाएं। यह कोड को स्पेगेटी की तरह दिखने से रोकता है जिसमें कई असंबद्ध सामान इंटरमीस्ड होते हैं।