スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#中級編 並列プログラミング(3) 例外

今回は並行プログラミング時の例外の紹介です。
 
まずは関数を定義しておきます。
> let throwExcFunc (id : int) () =
     failwith (sprintf "ID:%dで例外を発生させました" id)
     () ;;
 
val throwExcFunc : int -> unit -> unit
 
次に関数
> let tef1 = throwExcFunc 1;;
val tef1 : (unit -> unit)
 
として通常の形でtyr withを使って実行してみます。
 
> try 
    tef1 ()
with
| ex -> printfn "type = %A  mes = %A " (ex.GetType ()) ex.Message   ;;
 
type = System.Exception  mes = "ID:1で例外を発生させました" 
val it : unit = ()
 
それでは並行処理で実行してみます。
 
> try
    let task = Task.Factory.StartNew(fun () -> tef1 ())
    printfn "%d" task.Result
with
| ex -> printfn "type = %A  mes = %A " (ex.GetType ()) ex.Message ;;
type = System.AggregateException  mes = "1 つ以上のエラーが発生しました。" 
val it : unit = ()
 
ということでSystem.AggregateExceptionなるものが発生しました。
aggregateとは何というと「集合した」とかいう意味です。(ちなみにList.fold系の関数はaggregate系の関数とも言われます。)
なぜこのような例外が返ってくるようにしてあるかというと、スレッドからさらに複数のスレッドを呼び出し、それら中のスレッドから例外が発生した場合(場合によっては複数)のために、入れ子状になった(一つ以上の)例外を返せるメカニズムが必要となるわけです。
では、さきほどの例に戻りますが、実際の例外はAggregateExceptonのInnerExceptionsプロパティに保存されています。
ではちょっとコードを変えてみます。
 
> try
    let task = Task.Factory.StartNew(fun () -> tef1 ())
    printfn "%d" task.Result
with
| :? AggregateException as ae ->  
        for innerEx in ae.InnerExceptions do
            printfn "type = %A  mes = %A " (innerEx.GetType ()) innerEx.Message;;
 
type = System.Exception  mes = "ID:1で例外を発生させました" 
 
val it : unit = ()
 
では次に並列処理を入れ子にして,もっと深いところで例外を発生させてみます。
 

try
    let task = Task.Factory.StartNew(fun () -> let task1 = Task.Factory.StartNew(fun () -> tef1 ())
                                               printfn "%d" task1.Result //ここは実行されない
                                               task1.Result)//ここは実行されない 
            
    printfn "%d" task.Result
with    
| :? AggregateException as ae ->  
        for innerEx in ae.InnerExceptions do
            printfn "type = %A  mes = %A " (innerEx.GetType ()) innerEx.Message
 
;;
 
type = System.AggregateException  mes = "1 つ以上のエラーが発生しました。" 
 
val throwExcFunc : int -> unit -> int
val tef1 : (unit -> int)
 

ということで、呼び出し側のAggregateExceptionのInnerExceptionsより、さらに深いところで例外がthrowされているので、このような結果になります。さらに深いところまで下りて行って、結果を取ってくるには、Flattenメソッドか再帰処理を使います。
 
(1)Flattenメソッドを使った例
 

try
    let task = Task.Factory.StartNew(fun () -> let task1 = Task.Factory.StartNew(fun () -> tef1 ())
                                               task1.Result)//ここまでこない) 
            
    printfn "%d" task.Result //ここまでこない
with    
| :? AggregateException as ae ->  
        let t = ae.Flatten() //入れ子になった例外を一つのAggreateExceptionにまとめる
        for innerEx in t.InnerExceptions do
            printfn "type = %A  mes = %A " (innerEx.GetType ()) innerEx.Message;;
 
 
type = System.Exception  mes = "ID:1で例外を発生させました" 
val it : unit = ()
 
(2)再帰処理を行った例
 
> try
    let task = Task.Factory.StartNew(fun () -> let task1 = Task.Factory.StartNew(fun () -> tef1 ())
                                               task1.Result)//ここまでこない) 
            
    printfn "%d" task.Result //ここまでこない
with    
| :? AggregateException as ae ->
    let rec decAggregEx (ae : AggregateException) = //再帰関数の定義
        [
              for innerEx in ae.InnerExceptions do
              match innerEx with
                | :? AggregateException as ae -> yield! decAggregEx ae
                | _ -> yield innerEx
        ]
    decAggregEx ae
    |> List.iter (fun ex -> printfn "type = %A  mes = %A " (ex.GetType ()) ex.Message);;
 
 
type = System.Exception  mes = "ID:1で例外を発生させました" 
val it : unit = ()
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

F#中級編 並列プログラミング(2)

並行プログミングでは、一つの仕事をいくつかの独立な仕事に分けて、並行に走らせて後でそれをまとめるという操作をしていくことが基本ですが、それぞれの独立な仕事をTask Objectとして生成して処理していくのが、PFXのスタンダードな手法です。
System.Threading.TaskクラスはIAsynResultおよび、IDisposableインターフェイスを実装したクラスとして定義されています。
 
まずインスタンスを作るまえに、引数によって処理時間が変わる関数を一つ定義しておきます。
 
let longTaskSub r =
    let res = ref 0
    for i in 1 .. 100000 do
      for j in 1 .. 1000*r do
         res := !res + 1
    printfn "task %d end" r
    !res 
 
これは、「1を100000*1000*r回足し合わせて、その値を返す」という、時間ばかりかかるが特に意味はない関数です。
 
それではTaskクラスのインスタンスを一つ作ってみます。
 
let task0 = new Task<int>(fun () ->(longTaskSub 2) )
 
なおTaskの後の<int>は返り値としてintを返すという意味です。
なおコンストラクタも何種類か存在するのでMSDNのSystem.Threading.Tasks NamespaceのTask Classを調べてみてください。
 
実行はStartメソッドで行います。
 
task0.Start()
 
なお、Startメソッドは unit-> unit型なので、この時点で(通常は別スレッドで)並列処理が始まりますが、返り値がunitなので、結果は別の方法で取り出すことになります。
これにはResultプロパティを使用します。
これが優れもので、場合によって次のように異なる振る舞いをします。
(1)仕事が終わっていれば確保しておいた結果を返す。
(2)仕事が終わっていない場合は、結果が出るまで待ってその結果を返す。
 
実行例((1)の場合)
 
> let task0 = new Task<int>(fun () ->(longTaskSub 2) )
 
task0.Start()
longTaskSub 3 |> ignore //task0より処理に時間がかかる
printfn "%A" task0.Result;;
 
task 2 end
task 3 end
200000000
 
val task0 : Task<int>
 
実行例((2)の場合)
 
> let task0 = new Task<int>(fun () ->(longTaskSub 2) )
 
task0.Start()
longTaskSub 1 |> ignore //task0より処理に時間がかからない
printfn "%A" task0.Result;;
 
task 1 end
task 2 end
200000000
 
val task0 : Task<int>
 
上の例では、インスタンスの生成と開始を2STEPで行いましたが、これを1STEPで行うこともできます。
次のようになります。
 
let t = Task.Factory.StartNew<int>(fun () ->(longTaskSub 2) ) //new と startを一つのメソッドで行う
 
(注)この場合は<int>の部分はなくても大丈夫です。
> let t = Task.Factory.StartNew(fun () ->(longTaskSub 2) );;
 
val t : Task<int>
 
> task 2 end
 
再帰関数でTaskを使ってみます。
例としてはフィボナッチ数列の項を求めるもので、ふつうn=1,n=2のときは、直ちに1を返すのですが、
下の例ではここでlongTaskSub 1を計算して返すようにして計算を重くしています。
(単に再帰関数の中から、並行処理を開始する(スレッド内からスレッドを作成開始する)というサンプルで、プログラム的には特に意味があるものではありません。)
 
> let rec myBigFib n  parallellFlag  = //parallellFlagがtrueなら並行処理
    if n = 1 || n = 2 then
       longTaskSub 1
    else
       if parallellFlag = true then 
            let t1 = Task.Factory.StartNew<int>(fun () ->(myBigFib (n - 1) true) ) 
            let t2 = Task.Factory.StartNew<int>(fun () ->(myBigFib (n - 2)) true  ) 
            t1.Result + t2.Result 
       else
            (myBigFib (n - 1 ) false ) + (myBigFib (n - 2 ) false)  ;;
 
val myBigFib : int -> bool -> int
 
では使ってみます。
 
n = 7 で非並行の場合
> myBigFib 7 false;;
task 1 end
task 1 end
task 1 end
task 1 end
中略
task 1 end
リアル: 00:00:03.311、CPU: 00:00:03.312、GC gen0: 0, gen1: 0, gen2: 0
val it : int = 1300000000
 
n = 7 で並行の場合
> myBigFib 7 true;;
task 1 end
task task 1 end
1 end
task 1 end
中略
task 1 end
リアル: 00:00:01.103、CPU: 00:00:03.468、GC gen0: 0, gen1: 0, gen2: 0
val it : int = 1300000000
 
 
n = 10 で非並行の場合(なお返ってくる答えは桁あふれしているので無視してください。)
> myBigFib 10 false;;
task 1 end
task 1 end
中略
task 1 end
task 1 end
リアル: 00:00:13.982、CPU: 00:00:13.937、GC gen0: 0, gen1: 0, gen2: 0
val it : int = 1205032704
 
n = 10 で並行の場合
 
> myBigFib 10 true;;
task 1 end
中略
task 1 end
task 1 end
リアル: 00:00:04.151、CPU: 00:00:14.484、GC gen0: 0, gen1: 0, gen2: 0
val it : int = 1205032704
 
ということで約3倍のスピードとなってます。
 
(注意)再帰関数等で次々に並列なTaskを過剰に生成実行すると、スレッドの同期などのコストが高くなり、逆に高速化できなくなります。

テーマ : プログラミング
ジャンル : コンピュータ

F#中級編 並列プログラミング(1)

(注)この節のプログラムを実行するには.NET4.0以降が必要です。
 
並列プログラミングとは、計算をいくつかの部分に分割して、同時に計算しその結果をまとめて一つの結果を得るという手法です。
Parallel Extensions to the .NET Framework(PFX)を利用することにより.NET上で並列プログラミングを簡単に行うことができます。
 
まずはサンプル用に「少々処理に時間がかかる関数」を一つ定義しておきます。
 
let longTaskSub r =
    let res = ref 0
    for i in 1 .. 100000 do
      for j in 1 .. 10000 do
         res := !res + r
    !res 
 
これは引数に対しそれを100000*10000倍した値を返すという全く役にたたない関数です。
 
またタイマーをオンにしておきます。
> #time;;
--> 現在タイミングはオンです
 
それではこれらの関数に1,2...(n-1)を適用したものを配列に収めて表示してみます。
まずは並列プログラミングを使わない例です。
 
let notParallelSample n =
    let arr: int array = Array.zeroCreate n
    for i in 0 .. (n - 1) do
        arr.[i] <- longTaskSub i
    printfn "%A" arr
 
実行例
> notParallelSample 4;;
[|0; 1000000000; 2000000000; -1294967296|]
Real: 00:00:10.243, CPU: 00:00:10.234, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()//arr.[3]は桁あふれしていますが無視してください
 
それでは並列化してみます。
Parallel.Forを使うために
> open System.Threading.Tasks;;
としておきます。
 
 
let parallelSample n =
    let arr: int array = Array.zeroCreate n
    let rowTask i = 
        arr.[i] <- longTaskSub i
    Parallel.For(0,n,new System.Action<int>(rowTask)) |> ignore
    printfn "%A" arr
 
実行例
> parallelSample 4;;
[|0; 1000000000; 2000000000; -1294967296|]
Real: 00:00:02.924, CPU: 00:00:10.765, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()
 
nonParallelに比べてほぼ4倍の処理速度となってます。
Parallel.For の引数はループの始点と終点、(終点は含みません。)そしてloop変数を引数としunit型を返すdelegateであるSystem.Action<T>です。
 
また、関数を直接書いても、System.Action型に変換してくれます。例えば次のようになります。
 
let parallelSample2 n =
    let arr: int array = Array.zeroCreate n
    Parallel.For(0,n,fun i -> arr.[i] <- longTaskSub i)  |> ignore
    printfn "%A" arr
 
上の例ではParallel.Forの返り値をignoreしましたが、返り値としてはParallelLoopResult クラスのインスタンスが返ります。これはIsCompletedプロパティなどをもちます。
 
ForEachメソッドも準備されていて例えば次のように使用します。
 
let parallelSample3 n =
    let arr: int array = Array.zeroCreate n
    Parallel.ForEach([0;1;2;3],fun i -> arr.[i] <- longTaskSub i) |> ignore 
    printfn "%A" arr
    
さらに、もう一種類用意されているのがInvokeメソッドで、これはunit -> unit 型の関数をラップしたAction型の配列を引数にとります。例えば次のようになります。
 
let parallelSample4 n =
    let arr: int array = Array.zeroCreate n
    let actionArr : System.Action array = Array.init n (fun i -> (new System.Action (fun () -> arr.[i] <- longTaskSub i)))
    Parallel.Invoke actionArr
    printfn "%A" arr
    
これまでの、3つのメソッドすべてに共通して言えることですが、分割して行うそれぞれのタスクがどの順番で実行されるかは、こちら側では制御することはできません。よって分割して行うそれぞれのタスクが互いに実行順序に依存しない、すなわち「それぞれ独立」であることが肝要です。
(Arrayのイメージでいえば、map系はOKですが、fold系はダメという感じです。)
 
ということでArrayに対して、map系のメソッドについては並列処理のメソッドが準備されているので、次はこれを紹介したいと思います。
 
これはArray.Parallel モジュールにまとめられて次のようなものがあります。
 
Array.Parallel.choose
Array.Parallel.collect
Array.Parallel.init
Array.Parallel.iter
Array.Parallel.iteri
Array.Parallel.map
Array.Parallel.mapi
Array.Parallel.partition
 
使用方法はParallelがない場合と同様ですので割愛しますが、一番手軽に並列処理が利用できる方法ではないかと思います。
 
例 Array.Parallel.choose
 
> Array.Parallel.choose (fun i -> if (longTaskSub i) % 2 = 0 then Some(i) else None) [|1;2|];;
Real: 00:00:02.563, CPU: 00:00:05.125, GC gen0: 0, gen1: 0, gen2: 0
val it : int [] = [|1; 2|]
 
> Array.choose (fun i -> if (longTaskSub i) % 2 = 0 then Some(i) else None) [|1;2|];;
Real: 00:00:05.085, CPU: 00:00:05.078, GC gen0: 0, gen1: 0, gen2: 0
val it : int [] = [|1; 2|]

テーマ : プログラミング
ジャンル : コンピュータ

F#入門中級編第23回(Asynchronous Workflows(9) MailboxProcessor(4))

さてMailboxProcessorのまとめとして、id:suminさんが、どう書く?Orgに投稿された
居眠り床屋問題 http://ja.doukaku.org/285/
を取り上げてみたいと思います。
 
MailboxProcessorについては、今までに取り上げたメソッドの他に,CurrentQueueLengthプロパティを使用することにします。これはメッセージキューにいくつメッセージが残っているかを返します。
ではコードです。
 
open System
open System.Threading
 
type StylistAction =
| VisIDandTime of int*int  //客のIDと散髪所要時間
| FetchCutNumAndStop of AsyncReplyChannel<int> 
 
let stylist = new MailboxProcessor<StylistAction> (fun inbox ->
                let rec loop cutNum =
                  async{
                    let sleepState =
                        if inbox.CurrentQueueLength = 0 then //待っている客がいない
                            printfn "床屋、眠る"
                            true
                        else
                            false
                    let! msg = inbox.Receive()
                    match msg with
                       | VisIDandTime(id,t) ->
                            if sleepState then
                                printfn "床屋、目覚める"
                            printfn "散髪開始 %d" id
                            Thread.Sleep(t) //散髪所要時間
                            printfn "散髪完了 %d" id
                            return! loop (cutNum+1) 
                       | FetchCutNumAndStop(replyChannel) -> //終了時に送られてくる
                            printfn "床屋、眠る"
                            replyChannel.Reply(cutNum)
                            return ()
                       }
                loop 0)
 
 
let createVisAndIntervalAndSendVis (inStylist:MailboxProcessor<StylistAction>) allVisNum   =
   let rnd = new Random()
   let rec loop id =
       if id > allVisNum then
            let allCutNum = inStylist.PostAndReply(fun replyChannel -> FetchCutNumAndStop(replyChannel))
            printfn "%d人のうち %d人を散髪" allVisNum allCutNum
            ()
       else
         if id = 9 then
            Thread.Sleep(1200)
         else
            Thread.Sleep(rnd.Next(0, 200))
         
         if  inStylist.CurrentQueueLength > 1 then //Length=2 なら一人散髪中、二人待ち
            printfn "来店 %d\n満席で立ち去る %d" id id 
            loop (id + 1)
         else
            printfn "来店 %d " id 
            inStylist.Post(VisIDandTime(id,rnd.Next(100,400)))
            loop (id + 1)
                
   loop 1
 
stylist.Start()
createVisAndIntervalAndSendVis (stylist) 16
 
では実行してみます。
 
床屋、眠る
来店 1
床屋、目覚める
散髪開始 1
来店 2
散髪完了 1
散髪開始 2
来店 3
来店 4
来店 5
満席で立ち去る 5
来店 6
満席で立ち去る 6
来店 7
満席で立ち去る 7
散髪完了 2
散髪開始 3
来店 8
散髪完了 3
散髪開始 4
散髪完了 4
散髪開始 8
散髪完了 8
床屋、眠る
来店 9
床屋、目覚める
散髪開始 9
来店 10
来店 11
来店 12
満席で立ち去る 12
来店 13
満席で立ち去る 13
散髪完了 9
散髪開始 10
来店 14
来店 15
満席で立ち去る 15
散髪完了 10来店 16
満席で立ち去る 16
 
散髪開始 11
散髪完了 11
散髪開始 14
散髪完了 14
床屋、眠る
16人のうち 9人を散髪
 
ただ表示で「散髪完了 10」と改行の間に「来店 16」および「満室で立ち去る 16」が入りこんでますので、
dispAgentというMailboxProcessorを定義して、画面表示をこれに一本化したいと思います。改定版は次のようになります。
 
type DispAction =
| Str of string
| Stop of AsyncReplyChannel<unit> 
 
let dispAgent = MailboxProcessor<DispAction>.Start(fun inbox ->
                 let rec loop ()  =
                   async{
                    let! msg = inbox.Receive() 
                    match msg with
                    | Str(str) ->
                        printfn "%s" str
                        return! loop ()
                    | Stop(replyChannel) ->
                        replyChannel.Reply(())
                        return () 
                    }   
                 loop ())
 
 
type StylistAction =
| VisIDandTime of int*int  
| FetchCutNumAndStop of AsyncReplyChannel<int>
 
let stylist = new MailboxProcessor<StylistAction> (fun inbox ->
                let rec loop cutNum =
                  async{
                    let sleepState =
                        if inbox.CurrentQueueLength = 0 then
                            dispAgent.Post(Str("床屋、眠る"))
                            true
                        else
                            false
                    let! msg = inbox.Receive()
                    match msg with
                       | VisIDandTime(id,t) ->
                            if sleepState then
                                dispAgent.Post(Str( "床屋、目覚める"))
                            dispAgent.Post(Str("散髪開始 "+ id.ToString()))
                            Thread.Sleep(t)
                            dispAgent.Post(Str("散髪完了 "+ id.ToString()))
                            return! loop (cutNum+1) 
                       | FetchCutNumAndStop(replyChannel) ->
                            dispAgent.Post(Str("床屋、眠る"))
                            replyChannel.Reply(cutNum)
                            return ()
                       }
                loop 0)
 
 
let createVisAndIntervalAndSendVis (inStylist:MailboxProcessor<StylistAction>) allVisNum   =
   let rnd = new Random()
   let rec loop id =
       if id > allVisNum then
            let allCutNum = inStylist.PostAndReply(fun replyChannel -> FetchCutNumAndStop(replyChannel))
            let tempStr = sprintf "%d人のうち %d人を散髪" allVisNum allCutNum
            dispAgent.Post(Str(tempStr))
            let _ = dispAgent.PostAndReply(fun replyChannel -> Stop(replyChannel)) 
            ()
       else
         if id = 9 then
            Thread.Sleep(1200)
         else
            Thread.Sleep(rnd.Next(0, 200))
         
         if  inStylist.CurrentQueueLength > 1 then //Length=2 なら一人散髪中、二人待ち
            dispAgent.Post(Str("来店 "+ id.ToString()))
            dispAgent.Post(Str("満席で立ち去る "+ id.ToString()))
            loop (id + 1)
         else
            dispAgent.Post(Str("来店 "+ id.ToString()))
            inStylist.Post(VisIDandTime(id,rnd.Next(100,400)))
            loop (id + 1)
                
   loop 1
 
 
stylist.Start()
createVisAndIntervalAndSendVis (stylist) 16
 
実行例は次の通りです。
 
床屋、眠る
来店 1
床屋、目覚める
散髪開始 1
来店 2
来店 3
来店 4
満席で立ち去る 4
来店 5
満席で立ち去る 5
散髪完了 1
散髪開始 2
来店 6
来店 7
満席で立ち去る 7
来店 8
満席で立ち去る 8
散髪完了 2
散髪開始 3
散髪完了 3
散髪開始 6
散髪完了 6
床屋、眠る
来店 9
床屋、目覚める
散髪開始 9
来店 10
散髪完了 9
散髪開始 10
来店 11
来店 12
来店 13
満席で立ち去る 13
来店 14
満席で立ち去る 14
散髪完了 10
散髪開始 11
来店 15
来店 16
満席で立ち去る 16
散髪完了 11
散髪開始 12
散髪完了 12
散髪開始 15
散髪完了 15
床屋、眠る
16人のうち 9人を散髪

テーマ : プログラミング
ジャンル : コンピュータ

F#入門中級編第22回(Asynchronous Workflows(8) MailboxProcessor(3))

今回は前回作成したメッセージキューに整数を入れると、それらの和と、偶数だけの和を保持しながら働くというagentをクラス化してみたいと思います。
 
まずは前回のコードを再掲します。
 
type msg = 
    |Work of int
    |Stop
    |FetchAllSum of AsyncReplyChannel<int>
    |FetchEvenSum of AsyncReplyChannel<int>
 
let fetchable2wayAgent00 = MailboxProcessor.Start(fun inbox ->
                        let rec loop (allSum,evenSum) =
                            async{let! msg = inbox.Receive()
                                  match msg with
                                  | Work(i) ->
                                        let newAllSum = allSum + i 
                                        let newEvenSum =
                                            if i % 2 = 0 then
                                                evenSum + i
                                            else
                                                evenSum
                                        
                                        return! loop (newAllSum,newEvenSum) 
                                  | Stop ->
                                        return ()
                                  | FetchAllSum(replyChannel) -> 
                                        replyChannel.Reply(allSum) 
                                        return! loop (allSum,evenSum)
                                  | FetchEvenSum(replyChannel) ->
                                        replyChannel.Reply(evenSum)
                                        return! loop (allSum,evenSum)
                            }
                        loop (0,0))
                        
さて、それではこれをクラス化してみます。
 
>type msg = 
    |Work of int
    |Stop
    |FetchAllSum of AsyncReplyChannel<int>
    |FetchEvenSum of AsyncReplyChannel<int>
 
type fetchable2WayAgent() =
        let innerAgent =
                  MailboxProcessor.Start(fun inbox ->
                        let rec loop (allSum,evenSum) =
                            async{let! msg = inbox.Receive()
                                  match msg with
                                  | Work(i) ->
                                        let newAllSum = allSum + i 
                                        let newEvenSum =
                                            if i % 2 = 0 then
                                                evenSum + i
                                            else
                                                evenSum
                                        
                                        return! loop (newAllSum,newEvenSum) 
                                  | Stop ->
                                    return ()
                                  | FetchAllSum(replyChannel) -> 
                                        replyChannel.Reply(allSum) 
                                        return! loop (allSum,evenSum)
                                  | FetchEvenSum(replyChannel) ->
                                        replyChannel.Reply(evenSum)
                                        return! loop (allSum,evenSum)
                            }
                        loop (0,0))
        member this.PostInt(i) = innerAgent.Post(Work(i))
        member this.FetchAllSum() = innerAgent.PostAndReply(fun replyChannel -> FetchAllSum(replyChannel))
        member this.FetchEvenSum() = innerAgent.PostAndReply(fun replyChannel -> FetchEvenSum(replyChannel))
        member this.Stop() = innerAgent.Post(Stop);;
        
type msg =
  | Work of int
  | Stop
  | FetchAllSum of AsyncReplyChannel<int>
  | FetchEvenSum of AsyncReplyChannel<int>
type fetchable2WayAgent =
  class
    new : unit -> fetchable2WayAgent
    member FetchAllSum : unit -> int
    member FetchEvenSum : unit -> int
    member PostInt : i:int -> unit
    member Stop : unit -> unit
  end
    
では使ってみます。
 
> let agent0 = new fetchable2WayAgent();;
val agent0 : fetchable2WayAgent
 
> agent0.PostInt(1);;
val it : unit = ()
 
> agent0.PostInt(2);;
val it : unit = ()
 
> agent0.FetchAllSum();;
val it : int = 3
 
> agent0.FetchEvenSum();;
val it : int = 2
 
> agent0.Stop();;
val it : unit = ()

 
上の例ではnewでインスタンスを生成した時点で、非同期処理が開始されますが、別のタイミングで開始したい場合は、例えば次のようにします。(変更点は2行だけです。)
 
type msg = 
    |Work of int
    |Stop
    |FetchAllSum of AsyncReplyChannel<int>
    |FetchEvenSum of AsyncReplyChannel<int>
 
type fetchable2WayAgent2() =
        let innerAgent =
                  new MailboxProcessor<msg>(fun inbox -> //#この行を変更
                        let rec loop (allSum,evenSum) =
                            async{let! msg = inbox.Receive()
                                  match msg with
                                  | Work(i) ->
                                        let newAllSum = allSum + i 
                                        let newEvenSum =
                                            if i % 2 = 0 then
                                                evenSum + i
                                            else
                                                evenSum
                                        
                                        return! loop (newAllSum,newEvenSum) 
                                  | Stop ->
                                    return ()
                                  | FetchAllSum(replyChannel) -> 
                                        replyChannel.Reply(allSum) 
                                        return! loop (allSum,evenSum)
                                  | FetchEvenSum(replyChannel) ->
                                        replyChannel.Reply(evenSum)
                                        return! loop (allSum,evenSum)
                            }
                        loop (0,0))
        member this.Start() = innerAgent.Start() //#この行追加
        member this.PostInt(i) = innerAgent.Post(Work(i))
        member this.FetchAllSum() = innerAgent.PostAndReply(fun replyChannel -> FetchAllSum(replyChannel))
        member this.FetchEvenSum() = innerAgent.PostAndReply(fun replyChannel -> FetchEvenSum(replyChannel))
        member this.Stop() = innerAgent.Post(Stop)
 
使用例
 
> let agent2 = new fetchable2WayAgent2();;
val agent2 : fetchable2WayAgent2
 
> agent2.Start();;
val it : unit = ()
 
> agent2.PostInt(1);;
val it : unit = ()
 
> agent2.FetchEvenSum();;
val it : int = 0
 
> agent2.Stop();;
val it : unit = ()

テーマ : プログラミング
ジャンル : コンピュータ

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。