自律分散協調
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 2025 Paperzz