Eclipseでの非同期実行に関してはorg.eclipse.core.runtime.jobs.Jobというクラスを使います。
Jobの作成とタイプ
Jobの作成は、Jobを継承して実行処理を書くか、Job.createSystemかJob.createメソッドで作成します。個人的には後者がお勧めです。
Jobは以下の3種類の実行方法があります。GUIにどのように表示したいかによって、どれを使うかを決めて下さい。
種類 | 作り方 | 説明 |
---|---|---|
システムJob | createSystemで作るか、setSystem(true) | UIに表示されないバックグラウンドJob |
ユーザーJob | setUser(tru) | ダイアログが表示されるJob |
通常Job | UserにもSystemにもしない | Progressビューには表示されるが、ダイアログは出ないJob |
10秒待機するだけのJobを実行するだけのサンプルです。
Job systemJob = Job.createSystem(makeTask("System Job")); Job defaultJob = Job.create("Default Job!",makeTask("Default Job")); Job userJob = Job.create("User Job!",makeTask("User Job")); userJob.setUser(true); systemJob.schedule(); defaultJob.schedule(); userJob.schedule(); //----------------------------------------------- //10秒待つだけの処理を作る private ICoreRunnable makeTask(String jobName){ return monitor->{ try { monitor.beginTask(jobName, 10); int i=0; while(i!=10){ Thread.sleep(1000); monitor.worked(i); i++; if(monitor.isCanceled()){ break; } } } catch (InterruptedException e) { } monitor.done(); }; }
実行するとUser Job!のダイアログが出ます。また、ProgressにDefault Job!とUser Job!が表示されています。
Jobの実行ルール
あるJobが実行している間は他のJobをスタートしたくない場合があります。Jobはそういったルールを指定することが可能です。
IResourceはこのルールを実装していて、親子関係にあるIResourceをルールに持つJobが実行している間は、実行できなくすることが可能です。
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IProject aProj = root.getProject("A");// /A/ IFile file = aProj.getFile("File.txt");// /A/File.txt IProject bProj = root.getProject("B");// /B/ Job aJob = Job.create("A Project Job", makeTask("A Project Job")); Job fileJob = Job.create("File.txt Job", makeTask("File.txt Job")); Job bJob = Job.create("B Project Job", makeTask("B Project Job")); aJob.setRule(aProj); fileJob.setRule(file); bJob.setRule(bProj); aJob.schedule(); fileJob.schedule(); bJob.schedule();
A Project JobとB Project Jobは親子関係似ないので、同時に実行されています。一方、File.txt JobはA Project Jobと親子関係なので、A Project Jobが完了するまで待機状態になります。
なお、実行順序を逆にすると、今度はA Project JobがFile.txt Jobが完了するまで待機します。
fileJob.schedule(); aJob.schedule();
Ruleを自作する
ルールはISchedulingRuleを実装することで、自分で定義することも可能です。
メソッド | 説明 |
---|---|
contains | 引数が子であるかどうか。自分自身も子であると見なす |
isConflicting | 衝突関係にあるかどうか。衝突していない場合は実行可能。 |
ファイルパスなどの単一親の木構造ルールではなく、複数の親を持つルールを定義してみましょう。
public class MyRule implements ISchedulingRule{ private MyRule[] parents; public MyRule(MyRule... parents){ this.parents = parents!=null?parents:new MyRule[0]; } @Override public boolean contains(ISchedulingRule rule){ //※自分は必ずtrueにしなくてはならない。 if(rule == this)return true; if(rule instanceof MyRule){ for(MyRule r:((MyRule)rule).parents){ if(contains(r))return true; } } return false; } @Override public boolean isConflicting(ISchedulingRule rule){ if(rule instanceof MyRule){ MyRule other = (MyRule)rule; //※衝突ルールは双方向 // (子が実行していても親が実行していても衝突) return contains(other) || other.contains(this); } return false; } }
以下のようなテスト実行をしてみました。Job Bを遅延実行しています
MyRule a = new MyRule(); MyRule b = new MyRule(); MyRule ab = new MyRule(a,b); Job aJob = Job.create("Job A", makeTask("A")); Job bJob = Job.create("Job B", makeTask("B")); Job abJob = Job.create("Job [A/B]", makeTask("A/B")); aJob.setRule(a); bJob.setRule(b); abJob.setRule(ab); aJob.schedule(); bJob.schedule(1000); abJob.schedule(2000);
A,Bの両方が完了するまで、Job [A/B]が待機していることが確認できます。
Workspaceでアトミックな処理をするJobを作る
2005-04-12 - NetPenguinの日記によると、リソースを処理する作業は全てのプラグインにまたがって単一にしないと、失敗する場合があるそうです。(知らなかったー)そういった場合は、WorkspaceJobクラスを利用することでその目的を達成できます。
なお、Jobのように非同期に実行したくない場合は、ResourcePlugin.getWorkspace().runを使うことも出来ます。
Jobをグループ化する
複数のJobを走らせるとき、全てのJobが完了するまで待機したい、全てのJobを一気にcancelしたい場合などがあります。JobGroupでグループを設定することで、これらの目的を実装できます。
int maxThread = 2;//動作させる最大スレッド数 int seed = 1;//基本的に1にしておけば良い。 JobGroup group = new JobGroup("ab group", maxThread, seed); aJob.setJobGroup(group); bJob.setJobGroup(group); abJob.setJobGroup(group); aJob.schedule(); bJob.schedule(1000); abJob.schedule(2000); try { //全部のJobが終わるまで待機 //0にするとタイムアウトしない group.join(0, null); } catch (OperationCanceledException|InterruptedException e) { e.printStackTrace(); }
seedはGroupが管理する「初期」サイズで、初期値より多いJobをグループ化しても問題ありません。むしろ、初期値が大きすぎる方が問題で、初期サイズのJobが全て完了しない限りjoinで待機し続けることになります。
後から他のスレッドでJobを遅延定義すると言った場合でも無い限りは、seedは1にしておけば良いでしょう。
int seed = 4;// Jobの数(3個)より大きい //------------------------------- group.join(0, null);//永遠にjoinしない