自律分散協調 KMSF M1 tomotaka 今回のお題 • シングルプロセス内でのマルチスレッド協調 • Producer/Consumerパターンを実装してみま す • まずはサンプルプログラムをDownload – h:p://www.ht.sfc.keio.ac.jp/~tomotaka/mtpnf.zip – Eclipseにインポートしてください Producer/Consumer Pa:ern • 作る人/消費する人 Worker PUT Worker Task Queue Task Producer TAKE Worker Task Producer • タスクを作る人 • 今回のサンプルプログラムではここはシング ルスレッドです • タスクを作ってTask Queueに放り込むだけの仕 事しかしません Task Queue • Task Producer(Taskを作る人)とWorker(Taskを 消費する人)の間でTaskを受け渡すためのパ イプ役 • Thread Safeでなければいけない: – 極めて同じタイミングのTask PUTでも壊れない – 極めて同じタイミングのTask TAKEでも壊れない – 空のときのTAKEはPUTを待ってblockする – 満タンのときのPUTはTAKEを待ってblockする Worker(Task Consumer) • Task Queue経由でTaskを受け取り, 消化する 人 • 僕のPCにはデュアルコアCPUがのってる!! – 2つのWorkerを使うことで計算の効率を上げられるはず!! CoreBが暇 でもったいない! CoreA: 90% CoreB: 5% CoreA: 90% CoreB: 90% MulT Thread Programming on Java • 知っておくべきキーワード – Thread(class) – Runnable(interface) – synchronized(keyword) – wait() (method) – noTfy() (method) – noTfyAll() (method) ThreadとRunnableはわかるよね? Thread Sample class Mazu extends Thread { public void run() { System.out.println(“yabaissu”); } } Thread myMazu = new Mazu(); myMazu.start(); Runnable Sample class Mochi implements Runnable { public void run() { System.out.println(“Ramen Tsukemen Boku IKEMEN!!”); } } Mochi myMochi = new Mochi(); Thraed mochiThread = new Thread(mochi); mochiThread.start(); synchronized • synchronizedキーワードを持つメソッドは1インスタ ンスにつき、同時に1つしか起動できない(lockを 取得する) • 以下のようなhogeメソッドを使うと2行連続して hogeが出てくることはありえない // hogeとfugaの間に割り込めない public synchronized void hoge() { System.out.println(“hoge”); System.out.println(“fuga”); } wait • 各オブジェクトには待機中のスレッドがぶち込 まれるwait poolがある • wait()をたたいたスレッドはそのwaitのレシー バオブジェクトのwait poolに入る • noTfy(), noTfyAll()されるまでじっとしてる noTfy(), noTfyAll() • ロックを取得している(synchronizedメソッドの 中)で呼ぶとwait poolに入っているスレッドを wait poolから出す(けど、まだロックを譲りはし ない) • noTfy()は狙ったスレッドだけをwait poolから 出す • noTfyAll()は全部出す • Wait/noTfy/noTfyAllは概念が難しいのでTask Queueの実装のところで詳細に話すよ サンプルプログラムの説明 • Eclipseにインポートできた? • MulTThreadNumberFinder – メインプログラム, 主にProducer役 • Queue – Task Queue役, push/popというメソッドを備える • NumberFinder – Consumer役(Worker Thread), Queueからタスクを 取り出して処理をする Queueの動作1: push() • 値をqueueにつめる • synchronizedメソッド 1をPush Queue 1 Queueの動作2: pop() • FIFOなので現存する中で一番古くに詰められ た値を返す • synchronizedメソッド New Old 3 2 1 Pop 返り値は1 同時にpopしようとすると? • 同時にpopできない – pop()はsynchronizedだから! • 順番に実行されます 同時にpushしようとすると? • 同時にpushできない – push()はsynchronizedだから! • 順番に実行されます 空のときpopしようとすると? • キューが空であることを検知するととりあえず wait • これ以上減ることはない – pop()はsynchronizedだから! • Pushが実行されるとnoTfyAllされる – wait poolから抜け出せる – 抜け出たころにはキューの中身が増えている – 取り出せて値を返すことができる キューが満タンのときにpushしようと すると? • 満タンであることを検知するととりあえずwait • これ以上増えることはないことが保証されて いる – push()はsynchronizedだから! • pop()が実行されるとnoTfyAllされる – wait poolから抜け出ることができる – 抜けでたころにはキューの中身が減っている – 新しい値を詰めることができる なんとなくわかったところで動かす • MulTThreadNumberFinderを起動してください • 1から100までの数を1スレッドでナベアツがア ホになる数を探してみます • 1[Enter] • 100[Enter] • 1[Enter] • と入力 • 探す数の範囲やスレッドの数を増やしたり減ら したりして遊んでみよう 数の検査アルゴリズムを入れ替える • NumberFinderはQueueからタスクを取り出し、 一定のルールに合致する数を探すだけの働 きです • 「一定のルール」はNumberFindingAlgorithmオ ブジェクトによって定義されます • 僕があらかじめ二つ入れておきました • NabeatsuAlgorithm • PrimeNumberFindingAlgorithm 数の検査アルゴリズムを入れ替える • • • • • MulTThreadNumberFinder.javaを開いて algorithmTypeという変数の値を “PrimeNumberFindingAlgorithm” に変更! またいろいろパラメータを変えながら遊んでみ る 2つのアルゴリズムの実行時の差 • NabeatsuAlgorithm: – どちらかというとI/O Boundアルゴリズム – CPUタイムをあまり食わない – PopやnoTfyAllでCPUを食ってしまうため, スレッド が少ないほうが実行効率がいい • PrimeNumberFindingAlgorithm: – 完全なCPU Boundアルゴリズム – PopやnoTfyAllより実際の計算の方が重いのでス レッドを増やすとスループットがあがる 新しいアルゴリズムを実装せよ! • • • • • 1. NumberFindingAlgorithmを拡張するクラスを定義 2. NumberFindingAlgorithmFactoryを加筆修正 3. MulTThreadNumberFinderを加筆修正 実装したアルゴリズムの特徴を考察してみよう アルゴリズム案(思い浮かばない人用): – – – – 桁の数字全部を足したもので割り切れる数字(ex: 120, 280) 回文になっている数字(ex: 123454321, 101, 89098) 自分の誕生日のn乗になっている数(ex: =26214=45/12→512) 16進数に変換するとA Fの文字しか使わない数(ex: 255) • 実装できたらhjsにアップしたりしてみんなで共有してみよう • ひとり1個できたらおしまい
© Copyright 2024 Paperzz