Penjadwalan
Spons memperlihatkan :javadoc:`Penjadwal` untuk memungkinkan anda untuk menunjuk tugas-tugas yang akan dieksekusi di masa depan. Yang Penjadwal
menyediakan sebuah :javadoc:`Tugas.Pembangun` dengan mana anda dapat menentukan properti tugas seperti penundaan, selang, nama, (a)sinkronisitas, dan Runnable
(lihat tugas-properti).
Penyusun Halaman
Pertama, mendapatkan contoh dari Tugas.Pembangun
:
import org.spongepowered.api.scheduler.Task;
Task.Builder taskBuilder = Task.builder();
Satu-satunya diharuskan properti ini adalah Runnable, yang mana dapat anda tentukan menggunakan :javadoc:`Tugas.Pembangun#menjalankan(Runnable)`:
taskBuilder.execute(new Runnable() {
public void run() {
logger.info("Yay! Schedulers!");
}
});
atau menggunakan Java 8 sintaks dengan Tugas.Pembangun#menjalankan(Runnable runnable)
taskBuilder.execute(
() -> {
logger.info("Yay! Schedulers!");
}
);
atau menggunakan java 8 sintaks dengan Tugas.Pembangun#menjalankan(Konsumen<Task> tugas)
taskBuilder.execute(
task -> {
logger.info("Yay! Schedulers! :" + task.getName());
}
);
Blok properti
Dengan menggunakan Tugas.Pembangun
, anda dapat menentukan lain, properti opsional, seperti yang dijelaskan di bawah ini.
Properti |
Motede Dipakai |
Deskripsi |
---|---|---|
tundaan |
delayTicks(long delay)
|
Yang opsional jumlah waktu untuk lulus sebelum tugas adalah yang harus menjalankan. Waktu ditentukan sebagai jumlah kutu dengan delayTicks() metode, atau dapat diberikan sebagai jumlah waktu yang lebih nyaman unit dengan menentukan TimeUnit dengan delay() metode. Salah satu metode, tetapi tidak keduanya, dapat ditentukan per tugas. |
selang |
|
Jumlah waktu antara pengulangan tugas. Jika selang waktu tidak ditentukan, tugas tidak akan terulang. Waktu ditentukan sebagai jumlah kutu dengan intervalTicks() metode, atau dapat diberikan sebagai jumlah waktu yang lebih nyaman unit dengan menentukan TimeUnit dengan interval() metode. Salah satu metode, tetapi tidak keduanya, dapat ditentukan per tugas. |
sinkronisasi |
async() |
Sinkron tugas dijalankan dalam permainan ini loop utama di seri dengan centang siklus. Jika |
nama |
name(String name) |
Nama tugas. Secara default, nama tugas akan PLUGIN_ID " - " ( "- "|"S-" ) SERIAL_ID. Misalnya, default nama tugas bisa terlihat seperti "fooplugin-A-12". Ada dua tugas aktif akan memiliki yang sama serial ID yang sama jenis sinkronisasi. Jika nama tugas yang ditentukan, harus deskriptif dan membantu pengguna dalam debugging plugin anda. |
Terakhir, menyerahkan tugas scheduler menggunakan Task.Builder#submit(Object).
Dan hanya itu! Untuk meringkas, sepenuhnya fungsional tugas terjadwal yang akan berjalan asynchronously setiap 5 menit setelah awal keterlambatan dari 100 milidetik bisa dibangun dan disampaikan dengan menggunakan kode berikut:
import java.util.concurrent.TimeUnit;
Task task = Task.builder().execute(() -> logger.info("Yay! Schedulers!"))
.async().delay(100, TimeUnit.MILLISECONDS).interval(5, TimeUnit.MINUTES)
.name("ExamplePlugin - Fetch Stats from Database").submit(plugin);
Untuk membatalkan tugas, hanya menyebut :javadoc:`Tugas#batal()` metode:
task.cancel();
Jika anda perlu untuk membatalkan tugas dari dalam runnable itu sendiri, sebaliknya anda dapat memilih untuk menggunakan Konsumen<Task>` dalam rangka untuk mengakses task. Di bawah ini contoh akan menjadwalkan tugas yang akan menghitung mundur dari 60 dan batal dengan sendirinya setelah mencapai 0.
@Listener
public void onGameInit(GameInitializationEvent event) {
Task task = Task.builder().execute(new CancellingTimerTask())
.interval(1, TimeUnit.SECONDS)
.name("Self-Cancelling Timer Task").submit(plugin);
}
private class CancellingTimerTask implements Consumer<Task> {
private int seconds = 60;
@Override
public void accept(Task task) {
seconds--;
Sponge.getServer()
.getBroadcastChannel()
.send(Text.of("Remaining Time: "+seconds+"s"));
if (seconds < 1) {
task.cancel();
}
}
}
Asynchronous Tugas
Asynchronous tugas yang harus digunakan terutama untuk kode yang dapat mengambil jangka waktu yang signifikan untuk mengeksekusi, yaitu permintaan ke server lain atau database. Jika dilakukan di thread utama, permintaan ke server lain bisa sangat mempengaruhi kinerja dari permainan, karena berikutnya centang tidak bisa dipecat sampai request selesai.
Sejak Minecraft sebagian besar single-threaded, ada sedikit yang dapat anda lakukan dalam asynchronous benang. Jika anda harus menjalankan sebuah thread asynchronously, anda harus mengeksekusi semua kode yang tidak menggunakan SpongeAPI/mempengaruhi Minecraft, kemudian daftarkan lain sinkron tugas untuk menangani kode yang membutuhkan API. Ada beberapa bagian dari Minecraft bahwa anda dapat bekerja dengan asynchronously, termasuk:
Obrolan
Sponge's built-in Permissions penanganan
Sponge's scheduler
Selain itu, ada beberapa operasi lain yang aman untuk melakukan asynchronous:
Jaringan independen permintaan
Filesystem I/O (excluding files used by Sponge)
Peringatan
Mengakses permainan benda-benda di luar thread utama dapat menyebabkan crash, inkonsistensi dan berbagai masalah lainnya dan harus dihindari. Jika hal ini dilakukan salah, anda bisa mendapatkan ConcurrentModificationException dengan atau tanpa kecelakaan server terbaik dan rusak player/dunia/server yang paling buruk.
Kompatibilitas dengan aturan belanja lainnya
Sebagai plugin anda tumbuh dalam ukuran dan ruang lingkup anda mungkin ingin mulai menggunakan salah satu dari banyak concurrency perpustakaan, tersedia untuk Java dan JVM. Perpustakaan ini memang cenderung mendukung Java ExecutorService sebagai sarana untuk mengarahkan di mana benang tugas dijalankan.
Untuk memungkinkan perpustakaan ini untuk bekerja dengan Spons Scheduler
metode berikut dapat digunakan:
Scheduler#createSyncExecutor(Object) menciptakan :javadoc: SpongeExecutorService yang menjalankan tugas-tugas melalui Spons sinkron scheduler.
Scheduler#createAsyncExecutor(Object) creates a
SpongeExecutorService
which executes tasks through Sponge's asynchronous scheduler. Tasks are subject to the restrictions mentioned in Asynchronous Tasks.
Satu hal yang perlu diingat adalah bahwa tugas-tugas yang berinteraksi dengan Sponge di luar interaksi yang terdaftar di Asynchronous Tugas butuh untuk dieksekusi pada ExecutorService dibuat dengan Scheduler#createSyncExecutor(Objek) untuk menjadi thread-safe.
import org.spongepowered.api.scheduler.SpongeExecutorService;
SpongeExecutorService minecraftExecutor = Sponge.getScheduler().createSyncExecutor(plugin);
minecraftExecutor.submit(() -> { ... });
minecraftExecutor.schedule(() -> { ... }, 10, TimeUnit.SECONDS);
Hampir semua perpustakaan memiliki beberapa cara beradaptasi ExecutorService
untuk native jadwal tugas. Sebagai contoh paragraf berikut akan menjelaskan bagaimana ExecutorService
digunakan di sejumlah perpustakaan.
CompletableFuture (Java 8)
Dengan Java 8 CompletableFuture objek ditambahkan ke perpustakaan standar. Dibandingkan dengan ` masa Depan` objek, hal ini memungkinkan bagi pengembang untuk menyediakan callback yang disebut ketika selesai masa depan daripada memblokir benang sampai depan akhirnya selesai.
CompletableFuture adalah menguasai antarmuka yang biasanya memiliki tiga variasi untuk masing-masing fungsinya:
CompletableFuture#<function>Async(..., Executor ex)
Executes this function throughex
CompletableFuture#<function>Async(...)
Executes this function throughForkJoinPool.commonPool()
`` CompletableFuture # <function> (...) `` Jalankan fungsi ini pada thread apa pun yang sebelumnya `` CompletableFuture`` telah selesai.
import java.util.concurrent.CompletableFuture;
SpongeExecutorService minecraftExecutor = Sponge.getScheduler().createSyncExecutor(plugin);
CompletableFuture.supplyAsync(() -> {
// ASYNC: ForkJoinPool.commonPool()
return 42;
}).thenAcceptAsync((awesomeValue) -> {
// SYNC: minecraftExecutor
}, minecraftExecutor).thenRun(() -> {
// SYNC: minecraftExecutor
});
RxJava
`RxJava <https://github.com/ReactiveX/RxJava> ` _ adalah implementasi dari konsep `Reactive Extensions <http://reactivex.io/> ` _ untuk JVM.
Multithreading di Rx dikelola melalui berbagai Schedulers <http://reactivex.io/documentation/scheduler.html> ` _. Menggunakan ` Penjadwal # dari (Pelaksana Pelaksana) `` fungsi `` Pelaksana` yang disediakan oleh Sponge dapat diubah menjadi Penjadwal `` `.
Seperti CompletableFuture
secara default tindakan yang dilaksanakan pada benang yang sama yang melengkapi verifikasi identitas sebelumnya bagian dari rantai. Gunakan yang dapat Diamati#observeOn(Scheduler scheduler) untuk bergerak di antara benang.
Salah satu hal yang penting untuk diingat adalah bahwa akar Observable
akan dipanggil pada apa pun benang Observable#subscribe()``dipanggil. Jika akar dapat diamati berinteraksi dengan sponge itu harus dipaksa untuk berjalan serempak menggunakan ``Observable#subscribeOn(Scheduler scheduler)
.
import rx.Observable;
import rx.Scheduler;
import rx.schedulers.Schedulers;
SpongeExecutorService executor = Sponge.getScheduler().createSyncExecutor(plugin);
Scheduler minecraftScheduler = Schedulers.from(executor);
Observable.defer(() -> Observable.from(Sponge.getServer().getOnlinePlayers()))
.subscribeOn(minecraftScheduler) // defer -> SYNC: minecraftScheduler
.observeOn(Schedulers.io()) // -> ASYNC: Schedulers.io()
.filter(player -> {
// ASYNC: Schedulers.io()
return "Flards".equals(player.getName());
})
.observeOn(minecraftScheduler) // -> SYNC: minecraftScheduler
.subscribe(player -> {
// SYNC: minecraftScheduler
player.kick(Text.of("Computer says no"));
});
Scala
Scala hadir dengan built-in `Future <https://www.scala-lang.org/api/current/#scala.concurrent.Future> ` _ object yang banyak memiliki kerangka kerangka cermin dalam desain. Sebagian besar metode Masa Depan menerima sebuah `ExecutionContext <https://www.scala-lang.org/api/current/index.html#scala.concurrent.ExecutionContext;crwdn:ht:1:ht:crwdn;gt;`_ yang menentukan di mana bagian dari operasi dijalankan. Ini berbeda dengan CompletableFuture atau RxJava karena defaultnya dijalankan pada thread yang sama dengan operasi sebelumnya.
Fakta bahwa semua operasi mencoba untuk secara implisit menemukan ExecutionContext
berarti bahwa anda dapat dengan mudah menggunakan default ExecutionContext.global
dan secara khusus menjalankan bagian-bagian yang perlu menjadi thread-safe pada Spons server benang.
Untuk menghindari sengaja penjadwalan pekerjaan melalui Sponsge ExecutorContext
konteks lain harus secara implisit didefinisikan sehingga bertindak sebagai pilihan default. Untuk menjaga keselamatan thread hanya fungsi yang benar-benar berinteraksi dengan Spons akan perlu untuk memiliki Spons pelaksana yang ditentukan.
import scala.concurrent.ExecutionContext
val executor = Sponge.getScheduler().createSyncExecutor(plugin)
import ExecutionContext.Implicits.global
val ec = ExecutionContext.fromExecutorService(executor)
val future = Future {
// ASYNC: ExecutionContext.Implicits.global
}
future foreach {
case value => // SYNC: ec
}(ec)
future map {
case value => 42 // SYNC: ec
}(ec).foreach {
case value => println(value) // ASYNC: ExecutionContext.Implicits.global
}