WarCraft 3: Лимит операций (op-limit) и как создать поток вручную

Выполнение кода и обращение к данным
А теперь сюрприз! Каждый поток в Warcraft 3 имеет лимит операций. Как только выполнится определенное количество операций, варкрафт экстренно завершает поток и даже не сообщает об этом.
Скорее всего, это было сделано, чтобы избежать бесконечных циклов и различных ошибок зависания, допущеных дизайнерами во время написания карт Blizzard.
Подробнее про лимит операций с примерами можно посмотреть в теме XGM Forum - Прерывание потоков & limit op.

Что же теперь делать?!

За простые потоки волноваться не надо, а вот за поток инициализации различных баз данных стоит побеспокоиться и разбить его на новые потоки.

Как создать новый поток

Создать поток можно запуском одноразового таймера с минимальной задержкой, но в редких случаях я предлагаю не заморачиваться с таймерами и использовать функцию ExecuteFunc().
Простой пример:
function test3 takes nothing returns nothing
	много кода
endfunction

function test2 takes nothing returns nothing
	много кода
	call ExecuteFunc("test3")
endfunction

function test1 takes nothing returns nothing
	много кода
	call ExecuteFunc("test2")
endfunction

// ...
	call test1()
Пример с передачей параметров. Внимание: это работает хорошо только в Warcraft 3 и подобных! Как раз из-за того замечания про потоки.
На других, современных языках программирования появится проблема синхронизации данных.
globals
	unit test_receive_u = null
	int test_receive_i = 0
endglobals

function test_receive takes nothing returns nothing
	local unit u = test_receive_u
	local int i = test_receive_i
	
	// ваш код, глобальные в нем не используются,
	// потому что они могут быть перезаписаны другим потоком
	
endfunction

function test_send takes nothing returns nothing
	local unit u
	local int i
	
	// ваш код
	
	// передача аргументов в новый поток и создание нового потока
	set test_receive_u = u
	set test_receive_i = i
	call ExecuteFunc("test_receive")
	
	// Здесь внимание! Даже если вы вызовете TriggerSleepAction, 
	// новый поток продолжит выполняться. И это хорошо! =)
	
	// ваш код, например, обнуление (но не удаление) локальных юнитов
endfunction
Ещё два примера: раз, два.

Просмотров: 3 077

Diaboliko #1 - 5 лет назад 0
Давай краткие итоги наколдуй) Как Я понял, новый поток создается при звоне таймера, экзекуции и срабатывании события(при этом не совсем понятно взаимодействие системы событие-условие-действие)
Возможно для тестирования ради удовлетворения юзерами любопытства самостоятельно, следует добавить это в качестве кода, мол Желающие могут протестить и понять как это происходит ляляля....
ScorpioT1000 #2 - 5 лет назад (отредактировано ) 0
при этом не совсем понятно взаимодействие системы событие-условие-действие
что именно не понятно?
я могу написать, как я подозреваю сделана система событий внутри игры, но зачем это нужно)
Diaboliko #3 - 5 лет назад 0
Кто-то подкидывал такую инфу, что в условии (для ForGroup, at least) все работает быстрее. Грубо объясняю, но, думаю, суть ясна.
Зачем? За тем же, зачем и это
ScorpioT1000 #4 - 5 лет назад 0
Просто я не привык запоминать где условие где действие, т.к. я юзаю вейты и очень успешно.
*юзал, когда писал на варе =)
Darklight #5 - 5 лет назад 0
Поясните мне, пожалуйста:
  1. call ExecuteFunc - создаёт новый поток, который "ако бы " будет выполняться параллельно вызвавшему его коду. То есть, формально, код, написанный после ExecuteFunc может выполняться ещё до завершения выполнения кода вызванного через ExecuteFunc?
  2. Почему использование ExecuteFunc рекомендуется лишь в редких случаях?
  3. И как же всё-таки определить необходимость разделения кода на несколько потоков-вызовов?
ScorpioT1000 #6 - 5 лет назад (отредактировано ) 0
  1. в отличие от настоящей многопоточности, варкрафт делит вызовы операций не на кванты, как это делает ос, а на блоки кода между вызовами спец-прерываний вроде TriggerSleepAction, TimerStart итп, тоесть работают они вроде как последовательно, однако при вызове всяческих слипов сохраняется логика "параллельности". подробнее можно поискать на форуме по тегу ExecuteFunc
  2. из-за того, что ее вызов достаточно "дорогой". например, в цикле я бы ее не вызывал.
  3. добавлять дебаг-выводы и проверки, инициализировалась ли база до конца по истечение заданного отрезка времени (например вешать таймер и по событию проверить флаг. если нет, то поток оборвался и всем хальт))
Darklight #7 - 5 лет назад 0
ScorpioT1000, То есть, если я сделаю в тригере, скажем, бесконечный цикл, и не будут в нём вызывать ни ExecuteFunc, ни TriggerSleepAction, ни TimerStart и т.п. (интересно каков вообще полный список этого и т.п.), то у меня все остальные тригеры остановятся, пока варик сам не пришебёт полностью выполнение этого тригера с циклом по op-limit. Всё верно?
И ещё, в больших циклах или между большими блоками кода (без вышеуказанных прерывающих функций) можно насильно вставлять TriggerSleepAction(0.01) и тогда выполнение тригера будет временно прерываться и потом снова возобновляться - верно? И это не так же "дорого" для крутых алгоритмов тригера?
Темак #8 - 5 лет назад 0
да и да
Никто до сих пор не создал "прайс-лист" функций, и вряд-ли создаст, т.к. Nobody Cares и лень. Следовательно "стоимость" функций тебе нужно рассчитывать самому.
ScorpioT1000 #9 - 5 лет назад 0
Ну, так и есть, в принципе.
Darklight #10 - 5 лет назад 0
ScorpioT1000, Темак, а я, вроде, о другом сейчас говорил
ScorpioT1000 #11 - 5 лет назад 0
Со слипами я не пробовал продлевать поток, но похоже, у потока должен сохраняться счетчик операций, значит, он в итоге всеравно прибьётся.
Это сообщение удалено