2013-04-07 7 views
5

F #'de eşzamansız iş akışlarını göstermek için kullanılan yaygın bir örnek, birden fazla web sayfasını paralel olarak almaktır. Gelecekte bağlantı değişiklikleri durumunda Burada gösterilen http://en.wikibooks.org/wiki/F_Sharp_Programming/Async_Workflows Kodu: Buna bir örnek de verilmiştirGlobal durum ve F'deki Async İş Akışları F #

open System.Text.RegularExpressions 
open System.Net 

let download url = 
    let webclient = new System.Net.WebClient() 
    webclient.DownloadString(url : string) 

let extractLinks html = Regex.Matches(html, @"http://\S+") 

let downloadAndExtractLinks url = 
    let links = (url |> download |> extractLinks) 
    url, links.Count 

let urls = 
    [@"http://www.craigslist.com/"; 
    @"http://www.msn.com/"; 
    @"http://en.wikibooks.org/wiki/Main_Page"; 
    @"http://www.wordpress.com/"; 
    @"http://news.google.com/";] 

let pmap f l = 
    seq { for a in l -> async { return f a } } 
    |> Async.Parallel 
    |> Async.Run 

let testSynchronous() = List.map downloadAndExtractLinks urls 
let testAsynchronous() = pmap downloadAndExtractLinks urls 

let time msg f = 
    let stopwatch = System.Diagnostics.Stopwatch.StartNew() 
    let temp = f() 
    stopwatch.Stop() 
    printfn "(%f ms) %s: %A" stopwatch.Elapsed.TotalMilliseconds msg temp 

let main() = 
    printfn "Start..." 
    time "Synchronous" testSynchronous 
    time "Asynchronous" testAsynchronous 
    printfn "Done." 

main() 

ben bilmek istiyorum ne böyle bir ağ bağlantısı kaybı gibi küresel halde değişiklikleri ele gerektiğini nasıl? Bunu yapmanın zarif bir yolu var mı?

Şebeke durumunu Eşzamanlı arama yapmadan önce kontrol edebilir, ancak yürütme sırasında durum değişebilir. Birinin ne yapmak istediğini varsaymak, ağ başarısızlığa değil tekrar kullanılabilir hale gelene kadar yürütmeyi duraklatmaktı, bunu yapmak için işlevsel bir yol var mı? o kendilerini asenkron olarak uygulanmadı paralel ama operasyonlarda birden işlemleri çalıştırmak için Async.Parallel kullanır, böylece bu iplik iplik aşırı sayıda engelleme önlemek olmayacak - Her şeyden

cevap

4

Öncelikle örnekle bir sorun vardır havuzu.

Asenkron. Eğer WebClient ait AsyncDownloadString kullanabilmesi download ve downloadAndExtractLinks fonksiyonlar, çok asenkron olmalıdır, kod tamamen asenkron yapmak için:

let asyncDownload url = async { 
    let webclient = new System.Net.WebClient() 
    return! webclient.AsyncDownloadString(System.Uri(url : string)) } 

let asyncDownloadAndExtractLinks url = async { 
    let! html = asyncDownload url 
    let links = extractLinks html 
    return url, links.Count } 

let pmap f l = 
    seq { for a in l -> async { return! f a } } 
    |> Async.Parallel 
    |> Async.RunSynchronously 

yeniden deniyor. Şimdi, soruya cevap vermek için - ağ hatası gibi hataların ele alınması için yerleşik bir mekanizma yoktur, bu nedenle bu mantığı kendiniz uygulamanız gerekecektir. Doğru yaklaşım, durumunuza bağlıdır. Yaygın bir yaklaşım, işlemi belirli sayıda tekrar denemek ve istisnayı, ancak, örneğin, mesela; 10 kere. Diğer asenkron iş akışını alır ilkel olarak bu yazabilirsiniz:

let testAsynchronous() = 
    pmap (asyncRetry 10 downloadAndExtractLinks) urls 

Ortak devlet:

let rec asyncRetry times op = async { try return! op with e -> if times <= 1 then return (reraise e) else return! asyncRetry (times - 1) op } 

Sonra indirme 10 kez yeniden dener bir iş akışı oluşturmak için ana işlevi değiştirebilir.
Başka bir sorun ise, Async.Parallel sadece indirme işlemlerinin tamamlanmasından sonra geri dönecektir (eğer bir hatalı web sitesi varsa, beklemek zorunda kalacaksınız). Sonuçları geri döndüklerinde göstermek istiyorsanız, daha sofistike bir şeye ihtiyacınız olacak.

Bunu yapmanın iyi bir yolu, F # aracısını kullanmaktır - şu ana kadar elde edilen sonuçları saklayan bir aracı oluşturun ve iki iletiyi - yeni bir sonuç ekleyen ve mevcut durumu döndüren - diğerini işleyebilir. Ardından sonucu aracıya gönderecek birden çok zaman uyumsuz görevi başlatabilir ve ayrı bir eşzamansız iş akışında mevcut durumu kontrol etmek için yoklamayı kullanabilir (ve örneğin kullanıcı arayüzünü güncelleyebilirsiniz).

F # ajanlarıyla çok sayıda kod örneği olan developerFusion için MSDN series about agents ve twoarticles yazdım.

+0

Tom, F # ajanlarını gerçekten çok severken, bunun nasıl Haskell gibi işlevsel bir programlama olduğunu göremiyorum. Göründüğü gibi, bir devlete (Haskell'deki IO Monad) bir işleve geçecek bir şey olarak davranmak, devlete birden fazla aracı tarafından "eşzamanlı olarak" mutasyona uğratılacak bir şey olarak davranır ve ajanlar arasında iletilen mesajla hakemlik yapar. – JonnyBoats

+2

Aracıları kullanmak kesinlikle Haskell gibi işlevsel bir programlama değildir.Dürüst olmak gerekirse, soruna tamamen işlevsel çözümlerin zarif ve kullanışlı olduğunu düşünmüyorum. İleti geçişi eşzamanlılığı, F # içinde kullanılabilen başka bir kullanışlı paradigmadır - ve koordine edilmesi gereken eşzamanlı işlemler için gerçekten iyi çalıştığını düşünüyorum. –

+0

Bu, şu anda kafamı sokmaya çalıştığım bir şey. FP'yi Haskell gibi (ama yine de çok deneyimsiz) aracılığıyla keşfeden günah, F # de tamamen saf bir yaklaşıma gitmektir. Paradigmaların doğru karışımını bulmak uzun bir öğrenme süreci olacak, bence. – shambulator

İlgili konular