Zamanlayıcı
Sponge, gelecekte yürütülecek görevleri belirlemenize izin vermek için Scheduler öğesini açığa çıkarır. Scheduler
, gecikme, aralık, isim, eşzamanlılık(eşzamansızlık) ve Runnable
(bkz: Görev Özellikleri) gibi görev özelliklerini belirtebileceğiniz bir Task.Builder sağlar.
Görev Oluşturucu
Önce bir Task.Builder
örneği edinin:
import org.spongepowered.api.scheduler.Task;
Task.Builder taskBuilder = Task.builder();
Gerekli olan tek şey, Task.Builder#execute(Runnable) kullanarak belirtebileceğiniz Runnable özelliğidir:
taskBuilder.execute(new Runnable() {
public void run() {
logger.info("Yay! Schedulers!");
}
});
ya da Task.Builder#execute(Runnable runnable)
ile Java 8 sözdizimi kullanabilirsiniz:
taskBuilder.execute(
() -> {
logger.info("Yay! Schedulers!");
}
);
ya da Task.Builder#execute(Consumer<Task> task)
ile Java 8 söz dizimi kullanabilirsiniz:
taskBuilder.execute(
task -> {
logger.info("Yay! Schedulers! :" + task.getName());
}
);
Görev Özellikleri
Task.Builder
kullanarak, aşağıda tanımlandığı gibi başka, opsiyonel özellikler belirtebilirsiniz.
Özellik |
Kullanılan yöntem |
Açıklama |
---|---|---|
Gecikme |
delayTicks(uzun gecikme)
|
Görevden önce geçirilecek isteğe bağlı süre. Zaman Her iki yöntem de, fakat aynı anda olmamak değil, görev başına belirtilebilir. |
Aralık |
|
Görevin tekrarı arasındaki zaman miktarı. Bir aralık belirtilmemişse, görev tekrar edilmeyecektir. Zaman, Her iki yöntem de, fakat aynı anda olmamak değil, görev başına belirtilebilir. |
Eşitleme |
async() |
Eşzamanlı bir görev, oyun döngüsüyle seri ana oyun halkasında yürütülür. Eğer |
İsim |
İsim (dize ismi) |
Görevin adı. Varsayılan olarak görevin adı PLUGIN_ID “-” ( “A-” | “S-” ) SERIAL_ID olacaktır. Aynı senkronizasyon türü için iki aktif görev aynı iki seri ID’ye sahip olmamalıdır. Eğer görev adı belirlenmişse bunlar tanımlayıcı ve eklentinizin hata ayıklamasında kullanıcılara yardımcı olmalıdır. |
Son olarak, Task.Builder#submit(Object) kullanarak zamanlayıcıya görevi gönderin.
Ve bu kadar! Özetlemek gerekirse, 100 milisaniyelik bir başlangıç gecikmesinden sonra her 5 dakikada bir asenkron çalışacak tam işlevli bir zamanlanmış görev oluşturulabilir ve şu kodu kullanarak gönderilebilir:
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);
Bir görevi iptal etmek için, sadece Task#cancel() motodunu çağırmanız yeterli:
task.cancel();
Görevi çalıştırılabilirin kendisinden iptal etmeniz gerekiyorsa, görevi yerine getirmek için bir Consumer<Task>`
i seçebilirsiniz. Aşağıdaki örnek, 60’dan aşağı sayacak ve 0’a ulaştığında kendini iptal edecek bir görevi zamanlayacaktır.
@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();
}
}
}
Asenkron Görevler
Asenkron görevler, öncelikle yürütme süresi uzun olan, yani başka bir sunucuya veya veritabanına talepte bulunan alan kodlar için kullanılmalıdır. Ana mesaj dizisinde yapılırsa, bir sonraki onay işareti, istek tamamlanıncaya kadar tetiklenemez, çünkü başka bir sunucuya yapılan bir istek oyunun performansını büyük ölçüde etkiler.
Minecraft genellikle tek iplikli olduğundan eş zamanlı olmayan bir iplikte yapabileceğiniz çok bir şey yoktur. Eğer eş zamanlı olmayarak bir iplik çalıştırmanız gerekiyorsa SpongeAPI/affect Minecraft kullanmayan bütün kodları çalıştırmalı, sonra da API’ye ihtiyacı olan kodu çözmek için diğer bir synchronous görevi kaydetmelisiniz.Aşağıdakileri de içeren asynchronously ile birlikte çalışabileceğiniz bir çok Minecraft bölümü vardır:
Sohbet
Sponge’un yapısal İzinler yönetimi
Sponge’un programcısı
Buna ek olarak, asenkron olarak yapmak için güvenli olan birkaç operasyon daha vardır:
Bağımsız ağ istekleri
Filesystem I/O (excluding files used by Sponge)
Uyarı
Ana nesnenin dışındaki oyun nesnelerine erişmek çökmelere, tutarsızlıklara ve diğer çeşitli sorunlara neden olabilir ve kaçınılmalıdır. Eğer bu yanlış yapılırsa, en iyi durumda bir sunucu çöküşü olsun olmasın, en kötü ihtimalle bozulmuş bir oyuncu / dünya / sunucu olsun veya olmasın bir `` ConcurrentModificationException`` alabilirsiniz.
Diğer kütüphanelerle uyumluluk
Eklentiniz fiziksel olarak büyüdüğü için Java ve JVM için kullanılabilir olan bir çok aynı anda kullanılır kütüphaneden birini kullanmaya başlamak isteyebilirsiniz. Bu kütüphaneler görevin hangi iplikte çalıştırıldığına yöneltme yoluyla Java’nın ExecutorService desteklemeye eğilimlidir.
Bu kitaplıkların Sponge’un Scheduler
ile çalışmasına izin vermek için aşağıdaki yöntemler kullanılabilir:
Scheduler#createSyncExecutor(Object) Sponge’un eşzamanlı programcısı yoluyla görevleri çalıştıran bir SpongeExecutorService oluşturur.
Scheduler#createAsyncExecutor(Object), Sponge’ın eşzamansız zamanlayıcı ile görevleri yürüten bir
SpongeExecutorService
oluşturur. Görevler, ‘Asenkron Görevler’ bölümünde belirtilen kısıtlamalara tabidir.
Göz önünde bulundurulması gereken bir nokta, senkronize etkileşimlerin ‘Asynchronous Tasks’_ bölümünde listelenen etkileşimler dışında etkileşime giren görevlerin, iş parçacıklarına göre güvenli olması için``Scheduler#createSyncExecutor(Object)`` ile oluşturulmuş ExecutorService üzerinde yürütülmesi gerekir.
import org.spongepowered.api.scheduler.SpongeExecutorService;
SpongeExecutorService minecraftExecutor = Sponge.getScheduler().createSyncExecutor(plugin);
minecraftExecutor.submit(() -> { ... });
minecraftExecutor.schedule(() -> { ... }, 10, TimeUnit.SECONDS);
Neredeyse tüm kütüphanelerde, doğal görevleri zamanlamak için ExecutorService``ı uyarlamanın bazı yolları vardır. Örnek olarak aşağıdaki paragraflar, bir dizi kütüphanede ``ExecutorService
işlevinin nasıl kullanıldığını açıklayacaktır.
CompletableFuture (Java 8)
Java 8 ile CompletableFuture nesnesi standart kütüphaneye eklendi. Future
nesnesine kıyasla bu, geliştiriciye gelecek sonunda tamamlanıncaya kadar ipliği engellemek yerine geleceği tamamladığında çağırılan bir geri çağırma sağlar.
CompletableFuture akıcı bir arayüz olup, her fonksiyonu için genellikle aşağıdaki üç değişkene sahiptir:
CompletableFuture#<function>Async(…, Executor ex)`` bu fonksiyonu
ex
yoluyla çalıştırırCompletableFuture#<function>Async(...)
bu fonksiyonuForkJoinPool.commonPool()
yoluyla çalıştırırCompletableFuture#<function>(...)
bu fonksiyonu bir öncekiCompletableFuture
hangi iplik üzerinde tamamlandıysa orada çalıştırır.
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, JVM için`Reactive Extensions <http://reactivex.io/>`_ konseptinin bir uygulamasıdır.
Rx’deki çoklu kullanım çeşitli Schedulers ‘ler yoluyla yönetilir. Schedulers#from(Executor executor)
kullanmak Scheduler
a dönüşebilen Sponge tarafından sağlanan Executor
işlevini yerine getirir.
CompletableFuture
gibi varsayılan eylemler, zincirin önceki bölümünü tamamlayan aynı iş parçacığında yürütülür. İş parçacıkları arasında gezinmek için Observable#observeOn(Scheduler scheduler)
kullanın.
Unutulmaması gereken önemli bir nokta da, Observable#subscribe()
da çağrılmış herhangi bir iş parçacığı üzerinde Observable
kök çağrılmış olmasıdır. Kök gözlemci Sponge ile etkileşime giriyorsa, Observable#subscribeOn(Scheduler scheduler)
kullanarak eş zamanlı olarak çalıştırılmaya zorlanmalıdır.
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, çok sayıda skala çerçevesinin dizayn edildiği dahili bir Future nesnesi ile birlikte gelir. Geleceğin çoğu yöntemi hangi operasyonun nerede yürütüldüğünü `ExecutionContext <https://www.scala-lang.org/api/current/index.html#scala.concurrent.ExecutionContext;crwdn:ht:1:ht:crwdn;gt;`_ kabul etmektedir. Bu, önceki işlemin sona erdiği aynı iş parçacığında yürütülmeyi varsayılan olarak ayarlayan CompletableFuture veya RxJava’dan farklıdır.
Tüm bu işlemlerin dolaylı olarak bir ExecutionContext
bulmaya çalışması, varsayılan ExecutionContext.global
işlevini kolaylıkla kullanabileceğiniz ve Sponge sunucu iş parçacıklarında iş parçacıklığa karşı korunması gereken parçaların çalıştırıldığı anlamına gelir.
Yanlışlıkla Sponge `` ExecutorContext`` yoluyla çalışmayı önlemek için, başka bir bağlam örtük olarak tanımlanmalıdır, böylece varsayılan seçim işlevi görür. Mesaj dizisi güvenliğini sağlamak için, yalnızca Sponge ile gerçekte etkileşime giren işlevlerin Sponge çalıştırıcısının belirtilmesi gerekecektir.
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
}