提供: Minecraft Modding Wiki
移動先: 案内検索
(具体例)
(具体例)
117行目: 117行目:
 
==== 具体例 ====
 
==== 具体例 ====
  
 +
この方法は、非同期タスクを 3 秒のディレイ(遅延)経過後に 10 秒間隔で連続実行します。
 
This submits a task to an async thread to be executed after 3 seconds with a period of 10 seconds.  
 
This submits a task to an async thread to be executed after 3 seconds with a period of 10 seconds.  
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
122行目: 123行目:
  
 
   public void run() {
 
   public void run() {
       System.out.println("This message is printed by an async thread");
+
       System.out.println("このメッセージは非同期スレッドから出力されています。");
 
   }
 
   }
 
}, 60L, 200L);
 
}, 60L, 200L);
 
</source></blockquote>
 
</source></blockquote>
  
Both times count in server ticks. The initial delay is 60/20 = 3 seconds and the period is 200/20 = 10 seconds.
+
どちらの時間も Server Tick 単位の値のため、初回ディレイは 60/20 = 3秒、実行間隔は 200/20 = 10 秒となります。
 
+
上記のコードは、メッセージ "このメッセージは非同期スレッドから出力されています。" を10秒毎に出力します。
The above code will print "This message is printed by an async thread" every 10 seconds.
 
  
 
=== 戻り値を返すタスク ===
 
=== 戻り値を返すタスク ===

2013年4月7日 (日) 22:24時点における版

スケジューラの解説

この解説では、Bukkitのスケジューラの利用方法を説明しています。

主な注意事項

主な注意事項は次の2点です。

  • 他のスレッドからAPIのメソッドを(一部例外あり)を呼び出さない
  • メインのスレッドをスリープ状態にしてはならない

解説

スケジューラは次の3種類のタスクをオプションとして提供しています。

  • 遅延タスク(Delayed Task)
  • 繰り返しタスク(Repeating Task)
  • 戻り値を返すタスク

これらのタスクは2種類のスレッドとして実行する事ができます。

  • メインサーバスレッド
  • 専用スレッド

スレッドの種類

スケジューラにどのスレッドタイプを利用すれば良いかが、最も重要な問題です。
間違ったスレッドタイプを利用すると、検出が困難な障害を断続的に発生させる原因となります。
また、そのような障害は試験サーバでは発生せず、頻繁に連続稼動しているサーバでのみ発生する場合がある等、
再現性が低い障害となる場合もあるため、原因の特定を更に困難にします。

Minecraftサーバはマルチスレッドであり、
一つのメインサーバーのスレッドと、他のヘルパースレッドが存在します。

多くのデータ構造は、サーバのメインスレッドからのみアクセスされます。
別のスレッドからこれらのデータを変更しようとすると、
2つのスレッドが同時にデータにアクセスする事になるため、
データ破損を引き起こす可能性があります。

メインスレッドは、サーバーのCPU時間の配分を処理します。
このスレッドは、他のタスクへCPU時間を配分するために、秒間20回のスリープを実行します。

少数のBukkitAPIはスレッドセーフです。
あなたのプラグインが使っているAPIがスレッドセーフなものである事を確認していない限り、
基本的にBukkitAPIの命令はスレッドセーフではないものとして開発して下さい

同期タスク

サーバのメインスレッドが実行するタスクです。

重要: このスレッドが停止すると、サーバがフリーズします。


メインスレッドは下記の用途に使えます

  • スケジューラメソッドの呼び出し
    • スケジューラの戻り値を取得する際は注意を要します(後方の説明に記載)
  • 停止・待機処理を行わないタスク


メインスレッドは下記の用途に使わなくてはいけません

  • Bukkit APIのメソッドを呼び出すタスク(スレッドセーフな方法を除く)


メインスレッドは下記の用途に使ってはいけません

  • 自スレッドをスリープ状態にするタスク
    • 停止・待機処理を含むスレッド。(例:ネットワーク通信の受信タスク)


メインスレッド上で同期タスクを実行する事自体は良いのですが、
待機や停止の時間がほぼゼロであるものに限ります。

訳者注: 1つの処理ステップが長時間を費やす処理は、サーバ自体をその間待機状態に置いてしまうため、メインスレッド上で行うべきではない事を説明しています。

非同期タスク

非同期タスクは、サーバのメインスレッド以外のスレッドによって実行されます。専用のタスクとして実行されるため、同期タスクで説明したような問題を引き起こすことなく、自タスクを停止・スリープ状態にする事ができます。この特性は、同期タスクの真逆に相当します。


非同期タスクは下記の用途に利用できます

  • スケジューラメソッドの呼び出し
  • プラグインAPIを利用しないタスク


非同期タスクは下記の用途に利用しなければなりません

  • 自スレッドをスリープ状態にするタスク
    • 停止・待機処理を含むスレッド。(例:ネットワーク通信の受信タスク)


非同期タスクは下記の用途に利用してはいけません

  • Bukkit APIのメソッドを呼び出すタスク(スレッドセーフな方法を除いては、下記参照)


タスクの種類

タスクには、繰り返しタスク, 遅延タスク, 戻り値を返すタスク, の3種類があります。

APIのインタフェースの仕様については[こちら]を参照して下さい。

遅延タスク

一定の遅延を置いてから実行されるタスクです。遅延時間にはゼロを指定可能です。

具体例

この方法では、3秒のディレイ(遅延)経過後にプラグインのメインスレッド上でタスクを実行します。

myPlugin.getServer().getScheduler().scheduleSyncDelayedTask(myPlugin, new Runnable() {

   public void run() {
       getServer().broadcastMessage("This message is broadcast by the main thread");
   }
}, 60L);

ディレイには、 Server Tick 単位で 60 を指定しています。Server Tick は 1/20 秒に相当するので、今回の例では 60/20 = 3秒 となります。

This code could be executed by another thread. If the code

getServer().broadcastMessage("This message is broadcast by an async thread");

was executed by another thread, it could cause problems since the broadcastMessage method is being executed by another thread.

繰り返しタスク

繰り返し実行されるタスクです。遅延時間も設定可能です。

具体例

この方法は、非同期タスクを 3 秒のディレイ(遅延)経過後に 10 秒間隔で連続実行します。 This submits a task to an async thread to be executed after 3 seconds with a period of 10 seconds.

myPlugin.getServer().getScheduler().scheduleAsyncRepeatingTask(myPlugin, new Runnable() {

   public void run() {
       System.out.println("このメッセージは非同期スレッドから出力されています。");
   }
}, 60L, 200L);

どちらの時間も Server Tick 単位の値のため、初回ディレイは 60/20 = 3秒、実行間隔は 200/20 = 10 秒となります。 上記のコードは、メッセージ "このメッセージは非同期スレッドから出力されています。" を10秒毎に出力します。

戻り値を返すタスク

スケジューラから戻り値を返す事ができるタスクです。メインスレッドから実行可能なタスクです。

この仕組みの目的は、他タスクから呼ばれたAPIのメソッドに戻り値を返させる事です。

スケジューラは、戻り値として返されるであろうオブジェクトを取得可能にします。

具体例

Future<String> returnFuture = myPlugin.getServer().getScheduler().callSyncMethod(myPlugin, new Callable<String>() {

   public String call() {
       return "This is the string to return";
   }

});

try {
    // This will block the current thread 
    String returnValue = returnFuture.get();
    System.out.println(returnValue);
} catch (InterruptedException e) {
    System.out.println("Interrupt triggered which waiting on callable to return");
} catch (ExecutionException e) {
    System.out.println("Callable task threw an exception");
    ee.getCause().printStackTrace();
}

This will submit the callable and then use the Future.get() method. This method sleeps the current thread until the Callable has returned a value and gets the result.

メインスレッドからの利用

この方法は非推奨です。 この仕組みは、メインスレッドから利用される事を想定したものではありません。

.get()メソッドを利用している場合、メインスレッドがこのタスクを完了するまでの間、現行スレッド(=メインスレッド)をスリープ状態にします。

どうしてもメインスレッド上で使用する必要がある場合は、返されるであろうスケジューラの戻り値に対する.get()メソッドを呼び出す前に、.isDone()メソッドをチェックしなければなりません。さもなくばメインスレッドは、メインスレッド自身がタスクを完了させるまでスリープ状態になります。

Thread Safe API Methods

Bukkit API methods which are thread safe are: