2014-07-17 35 views
15

के संबंध में एएसआईसीसी-प्रतीक्षा सी # कोड को एफ # में अनुवाद करना मुझे आश्चर्य है कि यह एक व्यापक सवाल है, लेकिन हाल ही में मैंने खुद को कोड के एक टुकड़े में आने के लिए बनाया है, मैं इस बारे में निश्चित होना चाहता हूं कि कैसे अनुवाद करना है सी # उचित एफ # में। यात्रा here (1) (टीपीएल-एफ # इंटरैक्शन के साथ मूल समस्या) से शुरू होती है, और here (2) जारी है (कुछ उदाहरण कोड मैं एफ # में अनुवाद करने पर विचार कर रहा हूं)।शेड्यूलर

उदाहरण कोड यहां पुन: उत्पन्न करने के लिए बहुत लंबा है, लेकिन दिलचस्प कार्य ActivateAsync, RefreshHubs और AddHub हैं। विशेष रूप से दिलचस्प अंक

  1. AddHub का private async Task AddHub(string address) का हस्ताक्षर है।
  2. RefreshHubs एक पाश में AddHub कॉल और tasks की एक सूची है, जो यह तो await Task.WhenAll(tasks) से बहुत अंत में इंतजार कर रहा है और इसके परिणामस्वरूप वापसी मान private async Task RefreshHubs(object _) के अपने हस्ताक्षर से मेल खाता है एकत्र करता है।
  3. RefreshHubsActivateAsync द्वारा await RefreshHubs(null) के रूप में जाना जाता है और फिर अंत में await base.ActivateAsync() पर कॉल हस्ताक्षर public override async Task ActivateAsync() से मेल खाता है।

प्रश्न:

क्या एफ # है कि अभी भी इंटरफ़ेस और कार्यक्षमता को बनाए रखता है और डिफ़ॉल्ट, कस्टम अनुसूचक का सम्मान करता है करने के लिए इस तरह के समारोह हस्ताक्षर के सही अनुवाद हो सकता है? और मैं अन्यथा इस बारे में भी निश्चित नहीं हूं कि "async/f # में प्रतीक्षा करें" या तो। इसे "यांत्रिक रूप से" कैसे करें। :)

कारण यह है कि "यहां (1)" लिंक में समस्या है (मैंने यह सत्यापित नहीं किया है) कि F # async संचालन में एक कस्टम, सहकारी शेड्यूलर (ऑरलियन्स) द्वारा सेट नहीं किया गया है) रनटाइम। इसके अलावा, यह here कहा गया है कि टीपीएल ऑपरेशंस शेड्यूलर से बचते हैं और टास्क पूल पर जाते हैं और इसलिए उनका उपयोग प्रतिबंधित है। इस प्रकार

//Sorry for the inconvenience of shorterned code, for context see the link "here (1)"... 
override this.ActivateAsync() = 
    this.RegisterTimer(new Func<obj, Task>(this.FlushQueue), null, TimeSpan.FromMilliseconds(100.0), TimeSpan.FromMilliseconds(100.0)) |> ignore 

    if RoleEnvironment.IsAvailable then 
     this.RefreshHubs(null) |> Async.awaitPlainTask |> Async.RunSynchronously 
    else 
     this.AddHub("http://localhost:48777/") |> Async.awaitPlainTask |> Async.RunSynchronously 

    //Return value comes from here. 
    base.ActivateAsync() 

member private this.RefreshHubs(_) = 
    //Code omitted, in case mor context is needed, take a look at the link "here (2)", sorry for the inconvinience... 
    //The return value is Task. 
    //In the C# version the AddHub provided tasks are collected and then the 
    //on the last line there is return await Task.WhenAll(newHubAdditionTasks) 
    newHubs |> Array.map(fun i -> this.AddHub(i)) |> Task.WhenAll 

member private this.AddHub(address) = 
    //Code omitted, in case mor context is needed, take a look at the link "here (2)", sorry for the inconvinience... 
    //In the C# version: 
    //... 
    //hubs.Add(address, new Tuple<HubConnection, IHubProxy>(hubConnection, hub)) 
    //} 
    //so this is "void" and could perhaps be Async<void> in F#... 
    //The return value is Task. 
    hubConnection.Start() |> Async.awaitTaskVoid |> Async.RunSynchronously 
    TaskDone.Done 

startAsPlainTask समारोह here से साचा नाई से है

एक तरह से मैं इस के साथ काम कर के बारे में सोच सकते हैं एक एफ # समारोह के साथ है। मैं सिर्फ Task.WhenAll भी प्रतीक्षा कर रहे थे करने की आवश्यकता होगी देखा: एक और दिलचस्प विकल्प here रूप

module Async = 
    let AwaitTaskVoid : (Task -> Async<unit>) = 
     Async.AwaitIAsyncResult >> Async.Ignore 

< संपादित हो सकता है। लेकिन सही तरीका क्या होगा? उह, समय (एक बुरा यमक) सोने के लिए ...

< संपादित 2:here (1) पर (के साथ मूल समस्या TPL-एफ # बातचीत) Codeplex में यह उल्लेख किया गया था कि एफ # तुल्यकालन संदर्भों का उपयोग करता धागे के काम पुश करने के लिए , जबकि टीपीएल नहीं करता है। अब, यह एक व्यावहारिक स्पष्टीकरण है, मुझे लगता है (हालांकि कस्टम शेड्यूलर के बावजूद मुझे इन स्निपेट्स को सही ढंग से अनुवाद करने में समस्याएं हैं)।कुछ रोचक अतिरिक्त जानकारी से

मुझे लगता है मैं इस संदर्भ में Hopac उल्लेख करना होगा, एक दिलचस्प स्पर्शरेखा के रूप में लगता है और यह भी मैं अगले 50 अजीब घंटे या तो मामले में अपने सभी पार पोस्टिंग हाथ से जाने के लिए पहुंच से बाहर कर रहा हूँ का उल्लेख है।

< संपादित 3: Daniel और svick टिप्पणी में अच्छी सलाह एक कस्टम कार्य निर्माता का उपयोग करना दे। डैनियल एक ऐसे लिंक को प्रदान करता है जो पहले ही FSharpx में परिभाषित है।

स्रोत मैं देख रहा हूँ मानकों के साथ इंटरफेस

type TaskBuilder(?continuationOptions, ?scheduler, ?cancellationToken) = 
    let contOptions = defaultArg continuationOptions TaskContinuationOptions.None 
    let scheduler = defaultArg scheduler TaskScheduler.Default 
    let cancellationToken = defaultArg cancellationToken CancellationToken.None 

यदि एक ऑरलियन्स में इस का उपयोग करने के लिए गए थे के रूप में परिभाषित कर रहे हैं को देखते हुए, यह TaskScheduler की तरह लग रहा TaskScheduler.Current प्रति प्रलेखन के रूप में होना चाहिए here

ऑरलियन्स का अपना कार्य शेड्यूलर है जो अनाज के भीतर उपयोग किए गए एकल थ्रेडेड निष्पादन मॉडल प्रदान करता है। यह महत्वपूर्ण है कि कार्य करते समय ऑरलियन्स शेड्यूलर का उपयोग किया जाता है, न कि .NET थ्रेड पूल।

अपने अनाज कोड की आवश्यकता है एक उपकार्य बनाया जाना है, तो आप Task.Factory.StartNew उपयोग करना चाहिए:

का इंतजार Task.Factory.StartNew (() => {/ * तर्क * /});

यह तकनीक वर्तमान कार्य शेड्यूलर का उपयोग करेगी, जो ऑरलियन्स शेड्यूलर होगी।

आपको टास्क.रुन का उपयोग करने से बचना चाहिए, जो हमेशा .NET थ्रेड पूल का उपयोग करता है, और इसलिए सिंगल-थ्रेडेड निष्पादन मॉडल में नहीं चलाया जाएगा।

ऐसा लगता है कि TaskScheduler.Current और TaskScheduler.Default के बीच एक सूक्ष्म अंतर है। हो सकता है कि यह एक प्रश्न पूछता है जिसमें उदाहरण के मामलों में एक अवांछित अंतर होगा। ऑरलियन्स प्रलेखन बताते Task.Run उपयोग करने के लिए नहीं है और इसके बजाय Task.Factory.StartNew के लिए गाइड के रूप में, मुझे आश्चर्य है अगर एक के रूप में के रूप में Task.Run vs Task.Factory.StartNew पर स्टीफन Toub और StartNew is Dangerous पर स्टीफन Cleary इस तरह के अधिकारियों द्वारा की सिफारिश की है TaskCreationOptions.DenyAttachChild को परिभाषित करना चाहिए। हम्म, ऐसा लगता है कि .Default.DenyAttachChilld होगा जब तक कि मैं गलत नहीं हूं।

इसके अलावा, के रूप में वहाँ Task.RunTask.Factory.CreateNew अर्थात कस्टम अनुसूचक के बारे में के साथ एक समस्या है, मुझे आश्चर्य है अगर इस विशेष समस्या एक कस्टम TaskFactory रूप Task Scheduler (Task.Factory) and controlling the number of threads और How to: Create a Task Scheduler That Limits Concurrency में विस्तार से बताया उपयोग करके हटाया जा सकता है।

हम्म, यह पहले से ही काफी लंबे समय से "लंदन" बन रहा है। मुझे आश्चर्य है कि मुझे इसे कैसे बंद करना चाहिए?हो सकता है कि svick और डैनियल उनकी टिप्पणियों को उत्तर के रूप में बना सकता है और मैं svick's स्वीकार कर सकता हूं?

+2

मैं ऑरलियन्स उपयोग नहीं किया है, लेकिन यह एक कस्टम कार्य अनुसूचक एफ # में यह कोडिंग पर सभी async कोड चलाने की आवश्यकता है के बाद से 'async' का समर्थन नहीं करता सिर में दर्द होने जा रहा है। सबसे अच्छा आप कर सकते हैं (AFAIK) प्रत्येक 'कार्य 'को' Async <'T> 'में' Async.AwaitTask' (yuck) जैसे कुछ के साथ परिवर्तित कर देता है। – Daniel

+0

दरअसल, यही वह है जो मैं करने की कोशिश कर रहा था और बस एसिंक वर्कफ़्लो से बचें। हां, मुझे यकीन नहीं है कि मैं सफल हुआ हूं और किसी भी तरह से मैं कुछ सी # पैटर्न को एफ # में अनुवाद करने के तरीके पर थोड़ा सा शर्मिंदा हूं (उदाहरण के लिए मुझे 'async {}' 'का उपयोग करना चाहिए और यदि ऐसा है तो, या फिर फिर नहीं) । आम तौर पर मैं या तो भाषा में लिखता हूं और मुझे इसके बारे में वास्तव में सोचने की ज़रूरत नहीं है। – Veksi

+0

मुझे नहीं पता कि एफ # उस भाग पर टिप्पणी करने के लिए पर्याप्त है, लेकिन सिर्फ यह कहना चाहता था कि मूल "सी (कोड) नमूना से मूल सी # कोड में कुछ बग क्या हैं। 'फ्लश' को 'शून्य 'की बजाय' async कार्य 'विधि होना आवश्यक है क्योंकि' HubConnection.Start' और 'IHupProxy.Invoke' दोनों 'कार्य' को वापस करने वाले एसिंक विधियां हैं जिनकी प्रतीक्षा की जानी चाहिए। जब आप फोरच लूप के माध्यम से जाते हैं और कार्य करते हैं तो आप शायद कार्य एकत्र करना चाहते हैं। जब फ्लश विधि के अंत में। –

उत्तर

1

आप FSharpx में TaskBuilder का उपयोग कर सकते हैं और TaskScheduler.Current में पास कर सकते हैं। RefreshHubs अनुवाद करने का मेरा प्रयास यहां दिया गया है। ध्यान दें कि Task<unit> का उपयोग Task के बदले में किया जाता है।

let RefreshHubs _ = 
    let task = TaskBuilder(scheduler = TaskScheduler.Current) 
    task { 
     let addresses = 
      RoleEnvironment.Roles.["GPSTracker.Web"].Instances 
      |> Seq.map (fun instance -> 
       let endpoint = instance.InstanceEndpoints.["InternalSignalR"] 
       sprintf "http://%O" endpoint.IPEndpoint 
      ) 
      |> Seq.toList 

     let newHubs = addresses |> List.filter (not << hubs.ContainsKey) 
     let deadHubs = hubs.Keys |> Seq.filter (fun x -> 
      not (List.exists ((=) x) addresses)) 

     // remove dead hubs 
     deadHubs |> Seq.iter (hubs.Remove >> ignore) 

     // add new hubs 
     let! _ = Task.WhenAll [| for hub in newHubs -> AddHub hub |] 
     return() 
    } 
+0

यह एक अच्छा प्रयास है। :) मुझे लगता है कि मुझे इसकी जानकारी मिलती है (और सी # कोड कुछ "शून्य फंक्शनमुट्स इंटरनेशनलस्टेट()" सामान के साथ थोड़ा "ओह नहीं था"। सी # और कक्षाओं में भी पैरामीटर में पास करना और वापसी मूल्यों को इकट्ठा करना आसान लगता है और उन्हें कहीं असाइन करें। ओह ठीक है, यह एक अच्छा रन था। धन्यवाद! – Veksi

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