スポンサーサイト

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

F#による並列プログラミング入門 (3) Parallel Loops(2) ParallelLoopState Stop

 前回やったように、Parallelループは、「処理すべきindexの集合があって、いくつかのスレッドがそこから次々に数を取ってきては、スレッド上で関数(body部分)を実行する」というイメージです。 
 
ということで、それぞれの処理は、他の処理と独立に動作しているので、処理同士の通信手段がありません。これを回避するには、例えば 
 
let Dengonban = ref "続けろ" 
............. 
 
Parallel.For(1,100,(fun i -> if  !Dengonban = "やめろ" then .....      )) 
 
というように、すべての処理から参照可能な値を準備して、それを通じて行うことになります。 
ただ、この値を変更したりする場合は、競合状態に注意する必要がありますし、いちいち定義するのは手間ですので、あらかじめ、ParallelLoopStateクラスというのを.NET側で準備してくれています。これのインスタンスを使うことによって、安全に、ループ全体への伝言を残したり(ループ全体を制御したり)、ループ全体の状態を調べたりすることができます。 
 
 
ParallelLoopStateを使ってループ全体へ伝言できるものは次のようなものがあります。 
 
Break      Parallel ループの実行を現在の反復処理の終了後にできるだけ早く終了する必要があること を通知します。 
 
Stop        Parallel ループの実行をできるだけ早く終了する必要があることを通知します。 
 
 
ParallelLoopStateを使って調べることのできる状態は次のようなものがあります。 
 
IsStoped    ループのいずれかの反復処理が Stop を呼び出したかどうかを示す値を取得します。 
 
LowestBreakIteration  Break を呼び出したループの反復処理のうち、最も下位の反復処理を取得します。 
 
IsExceptional ループのいずれかの反復処理がその反復処理によってハンドルされない例外をスローしたかどうかを示す値を取得します。 
 
ShouleExitCurrentIteration  この反復処理または他の反復処理からの要求に基づいて、ループの現在の反復処理を終了する必要があるかどうかを示す値を取得します。 
 
では具体的例を紹介します。 
ParallelLoopStateを使用するには、Parallel.Forメソッドのオーバーロードのうち次の形のものを使います。 
 
static member For :  
        fromInclusive:int *  
        toExclusive:int *  
        body:Action<int, ParallelLoopState> -> ParallelLoopResult  
         
 
前回紹介したParallel.Forに対しこちらは、body部分がAction<int, ParallelLoopState>型の関数になってます。 
F#では、int -> ParallelLoopState -> unit型の関数を使えば、Action<int, ParallelLoopState>型の関数に変換してくれます。 
 
例えば次の様に使用します。 
 
> open System 
open System.Threading 
open System.Threading.Tasks;; 
 
> Parallel.For(1,10,(fun i loopState -> printfn "%d  " i )) ;; 
1   
7   
2   
8   
4   
9   
6   
3   
5   
val it : ParallelLoopResult = 
  System.Threading.Tasks.ParallelLoopResult {IsCompleted = true; 
                                             LowestBreakIteration = null;} 
 
 
上の例ではloopStateは関数の引数として出てきているだけで、関数内部では使用されていませんが、この形のParallel.Forを呼び出すことで、ループ全体の情報をつかさどるParallelLoopStateを関数内で使用することができます。(ParallelLoopStateのインスタンスは暗黙裡に生成され、このメソッドに渡されます。) 
またParallel.Forメソッドの返り値はParallelLoopResult型となっていて、これはParallelLoopStateとよく似ているのですが、プロパティがIsCompletedとLowestBreakIterationのみになっています。 
 
ではまずは、Stopメソッドを使用してみたいと思います。 
 
> let longTaskSub r =  
    let res = ref 0  
    for i in 1 .. 100000 do  
      for j in 1 .. 1000 do  
         res := !res + r  
    printfn "引数%3dに対しlongTaskSubの計算が終了" r 
    !res ;; 
 
val longTaskSub : int -> int 
 
> Parallel.For(1, 
             100, 
             (fun i (loopState:ParallelLoopState) ->  
                if ( i % 17 = 0) then 
                    printfn "i = %d でストップメソッドを呼び出します。" i 
                    loopState.Stop() 
                else 
                    longTaskSub i |> ignore ) 
             );; 
引数1に対しlongTaskSubの計算が終了 
引数25に対しlongTaskSubの計算が終了 
引数49に対しlongTaskSubの計算が終了 
引数26に対しlongTaskSubの計算が終了 
引数2に対しlongTaskSubの計算が終了 
引数50に対しlongTaskSubの計算が終了 
i = 51 でストップメソッドを呼び出します。 
引数97に対しlongTaskSubの計算が終了 
引数73に対しlongTaskSubの計算が終了 
引数27に対しlongTaskSubの計算が終了 
引数3に対しlongTaskSubの計算が終了 
val it : ParallelLoopResult = 
  System.Threading.Tasks.ParallelLoopResult {IsCompleted = false; 
                                             LowestBreakIteration = null;} 
                                       
 
上の例ではイテレーション値iが17の倍数である時に関数の第二引数であるParallelLoopStateオブジェクトに対してSoptメソッドを呼び出すことにより、ループ全体にStopすることを通知しています。 
通知した段階で、各スレッドは実行中の作業は最後までやり遂げますが、キューから新しい作業を取り出すことをやめます。 
また返り値であるParallelLoopResultのIsCompletedプロパティがfalseになっていることに留意してください。 
 
なお、Stopメソッドがどれかのスレッド内で呼び出された場合すぐに作業を中止する必要がある場合は、 
作業自体がParallelLoopStateの状態をチェックしながら実行するようにします。 
これには、「どれかのスレッドで、ParallelLoopStateオブジェクトに対してStopメソッドを呼び出した時点で、ParallelLoopStateオブジェクトのIsStoppedプロパティがtrueとなること」を利用します。 
例えば次のようにします。(実際にやっている作業は意味のない足し算なので、作業内容事態は無視してください。) 
 
> Parallel.For(1, 
             100, 
             (fun i (loopState:ParallelLoopState) ->  
                let mutable fState = " " 
                if ( i % 29 = 0) then 
                    printfn "i = %2d でストップメソッドを呼び出します。" i 
                    loopState.Stop() 
                    fState <- "ストップを発動" 
                else 
                    let res = ref 0  
                    for j in 1 .. 100000 do  
                        if(loopState.IsStopped = false)  then 
                            for k in 1 .. 1000 do  
                                res := !res + i 
                                fState <- "最後まで実行" 
                        else 
                           fState <- "途中でストップ"      
                    !res |> ignore 
                printfn "引数%3dに対し状況「%s」" i fState ) 
            );; 
引数 49に対し状況「最後まで実行」 
引数 25に対し状況「最後まで実行」 
引数  1に対し状況「最後まで実行」 
引数 73に対し状況「最後まで実行」 
引数 50に対し状況「最後まで実行」 
引数 26に対し状況「最後まで実行」 
引数 51に対し状況「最後まで実行」 
引数 74に対し状況「最後まで実行」 
引数 97に対し状況「最後まで実行」 
引数 27に対し状況「最後まで実行」 
引数 52に対し状況「最後まで実行」 
引数  2に対し状況「最後まで実行」 
引数 75に対し状況「最後まで実行」 
引数 28に対し状況「最後まで実行」 
i = 29 でストップメソッドを呼び出します。 
引数 29に対し状況「ストップを発動」 
引数 53に対し状況「途中でストップ」 
引数引数  3に対し状況「途中でストップ」 
 76に対し状況「途中でストップ」 
引数 98に対し状況「途中でストップ」 
val it : ParallelLoopResult = 
  System.Threading.Tasks.ParallelLoopResult {IsCompleted = false; 
                                             LowestBreakIteration = null;} 
 
Stopといっても並列処理では、イテレーター値iが順次実行されるわけではありませんから、「何か条件を満たすものが一つでも見つかれば後の処理をやめる」という文脈で使うべきかと思います。 
 
ParallelLoopはLoopというより、「イテレーター値を虫食い状態で消費していく」というイメージです。 
 
次回はBreakメソッドを紹介します。
(みなさんよいお年を)
スポンサーサイト

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

コメントの投稿

非公開コメント

良いお年をお迎えください

Dengonban は ref string 型なので、型が合いません

No title

訂正しました。ありがとうございます。BULEPIXYさんもよいお年をお迎えください。
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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