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)

delay(long delay,
TimeUnit unit)

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
intervalTicks(
rentang blok
interval(long interval,
TimeUnit unit)

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 Task.Builder#async digunakan, tugas akan berjalan asynchronously. Oleh karena itu, itu akan berjalan di thread sendiri, secara independen dari kutu siklus, dan mungkin tidak aman menggunakan permainan negara. (Lihat Asynchronous Tasks.)
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:

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 through ex
  • CompletableFuture#<function>Async(...) Executes this function through ForkJoinPool.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
}