[Решено] Многопоточность / Асинхронность

  • Автор темы Автор темы CoolBoy
  • Дата начала Дата начала
Статус
Закрыто для дальнейших ответов.

CoolBoy

Активный участник
Пользователь
Баллы
96
Имя в Minecraft
Xezard
Всем доброго времени суток, уважаемые форумчане.

Честно - очень редко задавался таким вопросом: а что такое многопоточность и каким образом её можно использовать с пользой, при написании своих плагинов?

Собственно, интересуясь этим вопросом я разворошил очень старенькие темы, тут, на рубакките, был на spigotmc.org, bukkit.org и других ресурсах связанных c java и майнкрафтом в целом. Проблема в том, что везде люди пишут разное. Кто-то пишет что нет смысла работать асинхронно, кто-то пишет, что, если это возможно, то нужно использовать асинхронность.
Также, я так и не смог найти примеров использования многопоточности в плагинах, ну или асинхронности, как вам удобно. Из-за этого создаётся весьма сомнительное впечатление о понятии "асинхронность".

Я хочу просто поинтересоваться/узнать, каким образом и где можно использовать асинхронные потоки и как, собственно, их использовать, если они не имеют доступа к Bukkit API?

Также, если это возможно, хотелось бы увидеть конкретные примеры использования асинхронности, желательно - с пояснениями.
 
Асинхронность - Не совпадающий с чем-либо во времени; неодновременный, несинхронный.
 
Ну это тебе должно помочь понять,что к чему
 
Ну это тебе должно помочь понять,что к чему
Я уже знаком с понятием, как таковым. Мне нужны примеры реализации - как правильно / как неправильно. Какой раз караю себя за выпрашивание готового, но найти на просторах интернета чего-то путного я так и не смог.
 
Какой раз караю себя за выпрашивание готового, но найти на просторах интернета чего-то путного я так и не смог.
Вот именно!!! а мне говоришь пиши,всегда лучше когда тебе дадут пример нормальный,ты разберешься в нем или тебе помогут и тогда все хорошо,а когда тебе просто текст дадут,типа на разбирай,то это не поможет тебе на практике.
 
асинхронные действия не имеют доступа к BukkitAPI -> их надо использовать, если у тебя есть какие-то довольно приличные по времени вычисления, которым не обязательно напрямую юзать апи. например, загрузить какую-нибудь приблуду из базы можно, считать что-нибудь с диска. если ты хочешь все действия игрока писать в базу, BlockBreakEvent вроде синхронный, но саму запись можно производить в асинхронном потоке, чтобы не виснул сервер.
 
Имеют. Асинхронно нельзя работать с миром и всем что с ним связано. В Bukkit есть "защита от дурка", но не везде.
очень важное замечание, которое все кардинально меняет. забираю все свои слова обратно.
 
Также, если это возможно, хотелось бы увидеть конкретные примеры использования асинхронности, желательно - с пояснениями.
Вот тебе примеры:) Мой менеджер прав rscPermissions использует сразу несколько потоков:
  1. По команде или по расписанию плагину нужно перечитать данные из таблиц в БД MySQL, для этого создаётся отдельный поток, который присоединяется к этой БД (а она может быть удалённой, вообще на другой стороне планеты), считывает оттуда все данные (а их может быть тоже немало). Всё это занимает много времени (относительно времени, доступного в пределах одного такта сервера). В худшем случае, если СУБД недоступен, то соединение как минимум подвисает на timeout (несколько) секунд. Нехорошо приостанавливать весь сервер ради того, чтобы ждать timeout к недоступному серверу.
  2. Когда все данные получены, происходит их фильтрация и нормализация (строки могут быть разбиты на несколько, часть может быть откинута). Происходит сохранение в файл кеша (.json).
  3. У меня есть грубо говоря HashSet<Player>, в который игроки добавляются при движении (PlayerMoveEvent и ещё несколько других). То есть в основной поток добавлено несколько EventHandler-ов, действия которых минимальны — добавление игрока в множество. Другой параллельный поток раз в секунду (настраивается) забирает всех игроков из этого множества и опустошает его. Затем он пробегается по списку игроков и проверяет, в каких регионах находится каждый игрок (игрок ведь может быть в нескольких регионах WG). Если с момента предыдущей проверки список регионов изменился, то принимается решение, что игроку нужно пересчитать все его права (какие-то из них могут пропасть или добавиться, если игрок перешел границу влияющего на права региона). Если нужно пересчитать, то объект Player помещается в очередь на пересчёт прав.
  4. Пересчётом прав занимается ещё один параллельный поток. Он постоянно висит на получении объекта Player из очереди, и как только получает, начинает достаточно ресурсоёмкую процедуру вычисления: это проход по множеству групп и прав, выяснение того, применимы ли они к игроку, вычисление префиксов/суффиксов и т.п. В итоге на руках имеется список пар String право и boolean значение. Наконец-таки на этот момент через шедулер планируется небольшой кусок кода, который на ближайшем такте работает с Bukkit API и заменяет для игрока его набор прав на новый пересчитанный.
Как можно заметить, при значительном объёме работы, выполняемой плагином, в потоке основного серверного цикла выполняются нерегулярные минимально необходимые кусочки кода, которые не в состоянии как-либо повлиять на TPS сервера.
 
Каким образом я могу сделать что-то в асинхронном потоке?
runTaskAsynchronously?
sheduleAsyncRepeating/DelayingTask?
new Thread?
Или можно запустить асинхронный поток, а в нём синхронный, в котором будет будут выполняться какие-либо действия с Bukkit API?

Немного нубских вопросов =)
 
  • Запуск нового потока: new Thread( ... ).start();. Нет смысла использовать для этого Bukkit API, потому что это просто лишняя прослойка.
  • Выполнить код из параллельного потока в основном: Bukkit.getScheduler().runTask( ... );. Выполняет переданный ему код на ближайшем серверном тике.
В целом, этого уже более, чем достаточно. Всё остальное это лишний сахар.

P.S. Я тут ещё рассуждал, когда сам учился работать с параллелью в Java.
 
Вот тебе примеры:) Мой менеджер прав rscPermissions использует сразу несколько потоков:
  1. По команде или по расписанию плагину нужно перечитать данные из таблиц в БД MySQL, для этого создаётся отдельный поток, который присоединяется к этой БД (а она может быть удалённой, вообще на другой стороне планеты), считывает оттуда все данные (а их может быть тоже немало). Всё это занимает много времени (относительно времени, доступного в пределах одного такта сервера). В худшем случае, если СУБД недоступен, то соединение как минимум подвисает на timeout (несколько) секунд. Нехорошо приостанавливать весь сервер ради того, чтобы ждать timeout к недоступному серверу.
  2. Когда все данные получены, происходит их фильтрация и нормализация (строки могут быть разбиты на несколько, часть может быть откинута). Происходит сохранение в файл кеша (.json).
  3. У меня есть грубо говоря HashSet<Player>, в который игроки добавляются при движении (PlayerMoveEvent и ещё несколько других). То есть в основной поток добавлено несколько EventHandler-ов, действия которых минимальны — добавление игрока в множество. Другой параллельный поток раз в секунду (настраивается) забирает всех игроков из этого множества и опустошает его. Затем он пробегается по списку игроков и проверяет, в каких регионах находится каждый игрок (игрок ведь может быть в нескольких регионах WG). Если с момента предыдущей проверки список регионов изменился, то принимается решение, что игроку нужно пересчитать все его права (какие-то из них могут пропасть или добавиться, если игрок перешел границу влияющего на права региона). Если нужно пересчитать, то объект Player помещается в очередь на пересчёт прав.
  4. Пересчётом прав занимается ещё один параллельный поток. Он постоянно висит на получении объекта Player из очереди, и как только получает, начинает достаточно ресурсоёмкую процедуру вычисления: это проход по множеству групп и прав, выяснение того, применимы ли они к игроку, вычисление префиксов/суффиксов и т.п. В итоге на руках имеется список пар String право и boolean значение. Наконец-таки на этот момент через шедулер планируется небольшой кусок кода, который на ближайшем такте работает с Bukkit API и заменяет для игрока его набор прав на новый пересчитанный.
Как можно заметить, при значительном объёме работы, выполняемой плагином, в потоке основного серверного цикла выполняются нерегулярные минимально необходимые кусочки кода, которые не в состоянии как-либо повлиять на TPS сервера.
Спасибо, но я уже успел пересмотреть все ваши плагины :)

  • Запуск нового потока: new Thread( ... ).start();. Нет смысла использовать для этого Bukkit API, потому что это просто лишняя прослойка.
  • Выполнить код из параллельного потока в основном: Bukkit.getScheduler().runTask( ... );. Выполняет переданный ему код на ближайшем серверном тике.
В целом, этого уже более, чем достаточно. Всё остальное это лишний сахар.

P.S. Я тут ещё рассуждал, когда сам учился работать с параллелью в Java.
Огромное спасибо =)
 
Статус
Закрыто для дальнейших ответов.
Назад
Верх