Zamanlayıcı
Uyarı
These docs were written for SpongeAPI 7 and are likely out of date. If you feel like you can help update them, please submit a PR!
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;
PluginContainer plugin = ...;
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();
If you need to cancel the task from within the runnable itself, you can instead opt to use a Consumer<Task>
in
order to access the task. The below example will schedule a task that will count down from 60 and cancel itself upon
reaching 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(Component.text("Remaining Time: "+seconds+"s"));
if (seconds < 1) {
task.cancel();
}
}
}
Uyarı
Any scheduled tasks should either watch the current state of the Game or should be unregistered if they are no longer needed (e.g. during a GameStoppingServerEvent). This is of particular importance for the client because it can start and stop the server multiple times.
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.
Since Minecraft is largely single-threaded, there is little you can do in an asynchronous thread. If you must run a thread asynchronously, you should execute all of the code that does not use SpongeAPI/affect Minecraft, then register another synchronous task to handle the code that needs the API. There are a few parts of Minecraft that you can work with asynchronously, including:
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.
Uyarı
Any scheduled tasks should either watch the current state of the Game or should be unregistered if they are no longer needed (e.g. during a GameStoppingServerEvent). This is of particular importance for the client because it can start and stop the server multiple times.
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;
PluginContainer plugin = ...;
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;
PluginContainer plugin = ...;
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;
PluginContainer plugin = ...;
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(Component.text("Computer says no"));
});
Scala
Scala comes with a built-in Future object which a lot of scala framework mirror in design. Most methods of the Future accept an ExecutionContext which determined where that part of the operation is executed. This is different from the CompletableFuture or RxJava since they default to executing on the same thread on which the previous operation ended.
The fact that all these operations try to implicitly find an ExecutionContext
means that you can easily use
the default ExecutionContext.global
and specifically run the parts that need to be thread-safe on the Sponge
server thread.
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
}