2014-05-12 17 views
5

को रद्द करते समय असंगत व्यवहार विभिन्न प्रकार के Asyncs को रद्द करते समय मुझे असंगत व्यवहार के साथ समस्याएं हैं।विभिन्न प्रकार के Asyncs

समस्या को पुन: उत्पन्न करने के लिए, मान लें कि एक कार्य है जो "नौकरियां" (Async < _> सूची) की एक सूची लेता है, उनके परिणामों को पूरा करने और प्रिंट करने की प्रतीक्षा करता है।

let processJobs jobs cancel = 
    Async.Start(async { 
    try 
     let! results = jobs |> Async.Parallel 
     printfn "%A" results 
    finally 
     printfn "stopped" 
    }, cancel) 

समारोह ऐसा कहा जाता है::

let jobs = [job1(); job2(); job3(); job4(); job5()] 
use cancel = new CancellationTokenSource() 

processJobs jobs cancel.Token 

और कुछ बाद में इसे रद्द कर दिया गया है:

Thread.Sleep(1000) 
printfn "cancelling..." 
cancel.Cancel() 

जब समारोह भी एक रद्द टोकन इसलिए इसे रद्द किया जा सकता है हो जाता है रद्दीकरण टोकन स्रोत रद्द कर दिया गया है, फ़ंक्शन को अंततः ब्लॉक निष्पादित करना चाहिए और "रोक दिया गया" प्रिंट करना चाहिए।

यह जॉब 1, 2 और 3 के लिए ठीक काम करता है, लेकिन सूची में जॉब 4 या जॉब 5 होने पर काम नहीं करता है।

Job1 सिर्फ Async.Sleeps:

let job1() = async { 
    do! Async.Sleep 1000000 
    return 10 
} 

Job2 उनके लिए कुछ async बच्चे और प्रतीक्षा करता है शुरू होता है:

let job2() = async { 
    let! child1 = Async.StartChild(async { 
    do! Async.Sleep 1000000 
    return 10 
    }) 

    let! child2 = Async.StartChild(async { 
    do! Async.Sleep 1000000 
    return 10 
    }) 

    let! results = [child1; child2] |> Async.Parallel 
    return results |> Seq.sum 
} 

Job3 कुछ बदसूरत इंतजार संभाल के लिए प्रतीक्षा करता है कि सेट है कुछ भी उग्र धागे द्वारा:

let job3() = async { 
    use doneevent = new ManualResetEvent(false) 

    let thread = Thread(fun() -> Thread.Sleep(1000000); doneevent.Set() |> ignore) 
    thread.Start() 

    do! Async.AwaitWaitHandle(doneevent :> WaitHandle) |> Async.Ignore 

    return 30 
} 

Job4 को पोस्ट और एक MailboxProcessor से एक उत्तर के लिए प्रतीक्षा करता है: एक टास्क (या TaskCompletionSource) के लिए

let job4() = async { 
    let worker = MailboxProcessor.Start(fun inbox -> async { 
    let! (msg:AsyncReplyChannel<int>) = inbox.Receive() 
    do! Async.Sleep 1000000 
    msg.Reply 40 
    }) 

    return! worker.PostAndAsyncReply (fun reply -> reply) // <- cannot cancel this 
} 

Job5 प्रतीक्षा करता है:

let job5() = async { 
    let tcs = TaskCompletionSource<int>() 

    Async.Start(async { 
    do! Async.Sleep 1000000 
    tcs.SetResult 50 
    }) 

    return! (Async.AwaitTask tcs.Task) // <- cannot cancel this 
} 

क्यों Job1, 2 और 3 रद्द कर दिए जाएंगे ("रुक गया" मुद्रित हो जाता है), जबकि जॉब 4 और 5 फ़ंक्शन को "हमेशा के लिए" लटकते हैं?

अब तक मैं हमेशा दृश्यों के पीछे रद्दीकरण को संभालने के लिए एफ # पर निर्भर करता हूं - जब तक मैं एसिंक-ब्लॉक में हूं और उपयोग करता हूं! (चलो !, करो !, वापसी!, ...) सबकुछ होना चाहिए ठीक है .. लेकिन यह हर समय मामला प्रतीत नहीं होता है।

Quote:

एफ # अतुल्यकालिक workflows में, CancellationToken वस्तु आड़ में स्वचालित रूप से चारों ओर पारित कर दिया है। इसका मतलब है कि हमें रद्दीकरण का समर्थन करने के लिए कुछ भी विशेष नहीं है। एसिंक्रोनस वर्कफ़्लो चलाते समय, हम इसे रद्दीकरण टोकन दे सकते हैं और सब कुछ स्वचालित रूप से काम करेगा।

पूरा कोड यहाँ उपलब्ध है: http://codepad.org/euVO3xgP

संपादित

मैंने देखा है कि Async.AwaitTask के बाद Async.StartAsTask के माध्यम से एक async पाइप यह सभी मामलों में रद्द करने योग्य बनाता है।

return! worker.PostAndAsyncReply (fun reply -> reply) 

लिए:

return! cancelable <| worker.PostAndAsyncReply (fun reply -> reply) 

रद्द किया जा रहा है के साथ:

let cancelable (x:Async<_>) = async { 
    let! cancel = Async.CancellationToken 
    return! Async.StartAsTask(x, cancellationToken = cancel) |> Async.AwaitTask 
} 

Job5 रद्द करने योग्य बनाने के लिए एक ही काम करता है

Job4 कि लाइन को बदलने का मतलब है के लिए

अर्थात।

लेकिन .. यह सिर्फ एक कामकाज है और मैं शायद ही कभी प्रत्येक कॉल को अज्ञात Async < _> पर डाल सकता हूं।

उत्तर

1

केवल Async। विधियों को डिफ़ॉल्ट रद्दीकरण टोकन का उपयोग करके संभाल लें।

अपने MailboxProcessor उदाहरण प्रारंभ विधि

let! ct= Async.CancellationToken 
use worker := MailboxProcessor.Start(theWork, ct) 

TaskCompletionSource उदाहरण में पर जाना चाहिए रद्द में, आप इसे रद्द करने के लिए एक कॉलबैक रजिस्टर करने के लिए जा रहे हैं।

let! ct = Async.CancellationToken 
use canceler = ct.Register(fun() -> tcs.TrySetCanceled() |> ignore) 
+0

मेलबॉक्सप्रोसेसर को रद्दीकरण टोकन पास करना। स्टार्ट केवल "वर्क" को रद्द कर देता है। लेकिन कार्यकर्ता.पोस्ट एंडएसिंक अभी भी ब्लॉक करता है और कभी वापस नहीं आता है। – stmax

संबंधित मुद्दे