2014-04-25 13 views
6

कैसे एक कोड एक अतुल्यकालिक WPF (या Windows फ़ॉर्म के लिए) घटना एफ # में हैंडलर? विशेष रूप से, क्या कोई कोडिंग पैटर्न है जो सी # 5 के एसिंक को अनुमानित करता है और प्रतीक्षा करता है?एफ # WPF के लिए अतुल्यकालिक ईवेंट हैंडलर्स सी # के async के समान और इंतजार

using System; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 

class Program 
{ 
    static int IncrementSlowly(int previous) 
    { 
     Thread.Sleep(3000); 
     if (previous == 2) throw new Exception("Oops!"); 
     return previous + 1; 
    } 

    static async void btn_Click(object sender, RoutedEventArgs e) 
    { 
     var btn = sender as Button; 
     btn.IsEnabled = false; 
     try 
     { 
      var prev = (int)btn.Content; 
      btn.Content = await Task.Run(() => IncrementSlowly(prev)); 
     } 
     catch (Exception ex) 
     { 
      btn.Content = ex.Message; 
     } 
     finally 
     { 
      btn.IsEnabled = true; 
     } 
    } 

    [STAThread] 
    static void Main(string[] args) 
    { 
     var btn = new Button() { Content = 0 }; 
     var win = new Window() { Content = btn }; 
     btn.Click += btn_Click; 
     new Application().Run(win); 
    } 
} 

मैं मुसीबत पता लगाना क्या बराबर एफ # का उपयोग कर किया जाएगा हो रहा है:

यहां एक संपूर्ण सी # WPF अनुप्रयोग है। मैंने async वर्कफ़्लो और Async विधियों के संयोजन का उपयोग करके कई प्रयास किए हैं। यह वास्तव में वास्तव में गन्दा असली तेजी से हो जाता है। मुझे आशा है कि एक आसान तरीका है कि मैं बस देख रहा हूं।

यहाँ मेरी प्रारंभिक बिंदु है, जो btn.Content <- incrementSlowly prev पर यूआई अप ताले है। अब मुझे आगे क्या करना है?

open System 
open System.Threading 
open System.Threading.Tasks 
open System.Windows 
open System.Windows.Controls 

let incrementSlowly previous = 
    Thread.Sleep(3000) 
    if previous = 2 then failwith "Oops!" 
    previous + 1 

let btn_Click (sender : obj) e = 
    let btn = sender :?> Button 
    btn.IsEnabled <- false 
    try 
     try 
      let prev = btn.Content :?> int 
      btn.Content <- incrementSlowly prev 
     with ex -> btn.Content <- ex.Message 
    finally 
     btn.IsEnabled <- true 

[<EntryPoint>][<STAThread>] 
let main _ = 
    let btn = new Button(Content = 0) 
    let win = new Window(Content = btn) 
    btn.Click.AddHandler(RoutedEventHandler(btn_Click)) 
    Application().Run(win) 

वैसे, मान लेते हैं कि incrementSlowly बदला नहीं जा सकता।

+0

इस लेख उपयोगी होना चाहिए http://tomasp.net/blog/csharp-fsharp- का प्रबंधन async-intro.aspx/ –

उत्तर

6

पहला कदम incrementSlowly अतुल्यकालिक बनाना है। यह वास्तव में अपने सी # कोड है, जो शायद एक अच्छा विचार नहीं है में तुल्यकालिक है - एक यथार्थवादी परिदृश्य में, इस नेटवर्क के साथ संचार किया जा सकता है, तो बहुत बार यह वास्तव में अतुल्यकालिक हो सकता है:

let incrementSlowly previous = async { 
    do! Async.Sleep(3000) 
    if previous = 2 then failwith "Oops!" 
    return previous + 1 } 

अब, आप कर सकते हैं बटन हैंडलर को असीमित भी क्लिक करें। हमें यकीन है कि है कि हम UI तत्व का उपयोग कर सकते बनाने के लिए बाद में Async.StartImmediate का उपयोग कर इसे शुरू करेंगे, तो हम अब के लिए dispatechers या यूआई धागे के बारे में चिंता करने की ज़रूरत नहीं है:

let btn_Click (sender : obj) e = async { 
    let btn = sender :?> Button 
    btn.IsEnabled <- false 
    try 
    try 
     let prev = btn.Content :?> int 
     let! next = incrementSlowly prev 
     btn.Content <- next 
    with ex -> btn.Content <- ex.Message 
    finally 
    btn.IsEnabled <- true } 

अंतिम चरण घटना पंजीकरण बदलने के लिए है।

btn.Click.Add(RoutedEventHandler(fun sender e -> 
    btn_Click sender e |> Async.StartImmediate) 

मुख्य बात Async.StartImmediate जो अतुल्यकालिक कार्यप्रवाह शुरू होता है: कुछ इस तरह चाल करना चाहिए। जब हम इसे यूआई थ्रेड पर कॉल करते हैं, तो यह सुनिश्चित करता है कि सभी वास्तविक कार्य UI थ्रेड पर किए जाते हैं (जब तक कि आप इसे पृष्ठभूमि में स्पष्ट रूप से ऑफ़लोड नहीं करते) और इसलिए आपके कोड में यूआई तत्वों तक पहुंच सुरक्षित है।

+0

मूल सी # कोड में, 'वृद्धि को धीरे-धीरे' सिंक्रोनस रखने और इसे नए थ्रेड में चलाने के दौरान आप इसे कैसे करेंगे? मुझे पता है कि एफ # में चीजें कैसे की जानी चाहिए, लेकिन मैं उत्सुक हूं;) –

+0

आप सी # में वही काम कर सकते हैं, लेकिन परिणामस्वरूप 'Async.AwaitTask' (और फिर' चलो! 'का उपयोग करें। मूल्य)।यह सी # कोड के समान व्यवहार को लागू करेगा। –

+0

मेरी वास्तविक परियोजना पर, मेरे पास धीमी विधि के लिए स्रोत तक पहुंच नहीं है। 'Async.AwaitTask' पर आधारित दृष्टिकोण बेहतर हो सकता है। – Wally

0

टॉमस सही ढंग से इंगित करता है कि यदि आप धीमी विधि को एसिंक्रोनस के रूप में परिवर्तित कर सकते हैं, तो let! और Async.StartImmedate खूबसूरती से काम करते हैं। यह पसंद है।

हालांकि, कुछ धीमी गति से तरीकों अतुल्यकालिक समकक्षों की जरूरत नहीं है। उस मामले में, Async.AwaitTask के टॉमस के सुझाव भी काम करता है। पूर्णता के लिए मैं एक और विकल्प का उल्लेख है, मैन्युअल Async.SwitchToContext साथ मार्शलिंग प्रबंध।

Async.AwaitTask एक नया टास्क

let btn_Click (sender : obj) e = 
    let btn = sender :?> Button 
    btn.IsEnabled <- false 
    async { 
    try 
     try 
     let prev = btn.Content :?> int 
     let! next = Task.Run(fun() -> incrementSlowly prev) |> Async.AwaitTask 
     btn.Content <- next 
     with ex -> btn.Content <- ex.Message 
    finally 
     btn.IsEnabled <- true 
    } 
    |> Async.StartImmediate 

मैन्युअल धागा संदर्भ

let btn_Click (sender : obj) e = 
    let btn = sender :?> Button 
    btn.IsEnabled <- false 
    let prev = btn.Content :?> int 
    let uiContext = SynchronizationContext.Current 
    async { 
    try 
     try 
     let next = incrementSlowly prev 
     do! Async.SwitchToContext uiContext 
     btn.Content <- next 
     with ex -> 
     do! Async.SwitchToContext uiContext 
     btn.Content <- ex.Message 
    finally 
     btn.IsEnabled <- true 
    } 
    |> Async.Start 
संबंधित मुद्दे