SC2Mapster Wiki
Explore
Main Page
All Pages
Interactive Maps
navigation
Main page
Community portal
Recent changes
Random page
Help
SC2MAPSTER
News
Maps
Assets
Teams
Authors
Forums
Paste
Gamepedia
Gamepedia support
Report a bad ad
Help Wiki
Contact us
FANDOM
Fan Central
BETA
Games
Anime
Movies
TV
Video
Wikis
Explore Wikis
Community Central
Start a Wiki
Don't have an account?
Register
Sign In
Sign In
Register
SC2Mapster Wiki
1,003
pages
Explore
Main Page
All Pages
Interactive Maps
navigation
Main page
Community portal
Recent changes
Random page
Help
SC2MAPSTER
News
Maps
Assets
Teams
Authors
Forums
Paste
Gamepedia
Gamepedia support
Report a bad ad
Help Wiki
Contact us
Editing
Galaxy/Triggers/Multithreading and AI
(section)
Back to page
Edit
VisualEditor
View history
Talk (0)
Edit Page
Galaxy/Triggers/Multithreading and AI
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==Original Article From Legacy Wiki== *'''Requires salvage of any still relevant points''' ====Multithreading==== The Galaxy virtual machine is capable of supporting multiple threads of execution. These are very similar to threads in other programming languages providing both separate stack space and instruction pointers. Threads are created by running a trigger object either in response to an event or by another thread running the trigger. Threads are automatically destroyed upon return of the root function. It should be noted that there is a maximum limit to concurrent threads (what amount?) after which attempting to create more will fail and generate a script error. Thread execution is controlled by a scheduler inside the Galaxy virtual machine. When a thread is scheduled it is given a fixed maximum run time in the form of an operation limit in order to prevent infinite loops from hanging the session. If a thread fails to die or de-schedule by the time the operation limit is reached it is forcefully terminated and a trigger error is generated. There are a variety of ways threads can be de-scheduled. Creating other threads by executing triggers and waiting for completion allows sub-frame rescheduling to occur if the resulting threads do not de-schedule (unsure). Calling the Wait native will suspend thread execution for a number of frames determined by the specified timeout. Any native that performs synchronization of client side values will suspend thread execution until the server synchronizes the value. It should be noted that multi-threading will not improve execution performance in anyway. The scheduler used by the Galaxy virtual machine is non-preemptive and only has a single execution unit. The reason for this is to produce a very clear and strict memory model which is free of race conditions so easy to use. It is believed that the Galaxy virtual machine physically runs on the deterministic thread of StarCraft II so shares processor time with all deterministic tasks. Performance critical code should avoid excessive thread creation due to the extra time overhead it adds. Although race conditions do not exist in the memory model, they still can occur when dealing with artificial resources. An artificial resource is any concept finite in quantity that a thread may need to use non-instantly. A common example of such a resource is the client display area. Showing 2 dialogs with different choices at the same time over the same area of client display is not helpful so a lock could be used blocking another choice from being shown by another thread until the first choice is dealt with. This would allow the same resources to be shared between both dialogs in a common pool improving efficiency as well. One common purpose of creating threads is to mitigate the operation limit placed by the scheduler.This can be useful for very operation intensive code that executes fast and cannot complete within the operation limit provided. Care must be taken when doing this to avoid hanging clients or causing excessive stalling by executing too much script in a single frame. Consider staggering intensive operations over several frames to spread processor time consumption more evenly. Optimization can be an alternative but has limits. Another useful feature of threads is the dynamic nature of the thread stack. Galaxy does not support dynamic memory allocation with good performance so any form of fast dynamic memory can be very useful. A thread could represent a persistent object with object state retained entirely on stack in the form of local variables. These can then be created as required and left to function autonomously until natural thread death. Only fairly complex and rare objects should use this to avoid hitting the maximum thread limit. ====Action Definitions==== In GUI an easy way to create new threads is by using Action Definitions. Under "options" selecting "Create Thread" will cause the Galaxy Virtual machine to run the specified actions in a new thread when ever the action is called. This is useful when dealing with code that may block so that the caller function can continue execution. It is also useful when dealing with operation intensive code being run in a dynamic loop that could cause an operation limit thread crash if the loop becomes too large. It should be avoided for performance critical or commonly called actions as creating threads does have an overhead. The way GUI implements these actions in Galaxy is to attach a function to a trigger object. Parameters get placed in register like global variables before executing the trigger to create a new thread. The executing function copies the parameters from the global into local variables (stored on the stack) allowing the same call procedure to be applied multiple times even if suspended threads executing the function exist. ====Locks==== Locks are used to prevent race conditions when manipulating a finite resource. Locks are only needed for non-instant code which might run into conflicts from multiple threads and the way they are implemented using Wait can cause threads to be de-scheduled. They should be avoided when possible as they can result in un-bounded thread queuing for highly contended resources which may eventually hit the maximum thread limit. This can be avoided by using a pool of resources to allow multiple threads access to required resources at the same time. An example of for locking could be when an integer is used to determine critical session state such as in an RPG map or party map. To change this state actions are created for when a boss is killed and when a town is entered. They manipulate a single global state integer to various values for a period of time. Boss Kill Set variable i = 0 Wait 60 game seconds Set variable i = 10 Enter Town Set variable i = i + 1 Wait 30 game seconds Set variable i = i - 1 If both a player were to kill the boss and enter a town at the same time the following could happen: Enter Town is scheduled first so i is set to some value. Enter Town de-schedules for 30 seconds. Boss Kill is scheduled next so i set to 0. The value from Enter Town has been lost. Boss Kill de-schedules for 60 seconds. 30 seconds pass (480 game frames). Enter Town is re-scheduled so i is set to -1. This might not be an expected value. 30 seconds pass (480 game frames). Boss Kill is re-scheduled so i is set to 10. Might go back to normal, or maybe the session has bugged horribly. The values are clearly not doing what is desired as a -1 popped up which might cause unexpected things to occur. In this case using a lock action can help as you can assure that only a single thread will be manipulating the value in a consistent way. To do this, create a new global boolean variable "Lock i". Then use the "Critical Section" trigger action, like this: Boss Kill General - Enter critical section using Lock i Actions Set variable i = 0 Wait 60 game seconds Set variable i = 10 Enter Town General - Enter critical section using Lock i Actions Set variable i = i + 1 Wait 30 game seconds Set variable i = i - 1 If both a player were to kill the boss and enter a town at the same time the following could happen: Enter Town is scheduled first so grabs the lock and i is set to some value. Enter Town de-schedules for 30 seconds. Boss Kill is scheduled next but cannot grab the lock so is de-scheduled. 30 seconds pass (480 game frames). Enter Town is re-scheduled so i is set to the original value. Boss Kill is re-scheduled as it can grab the lock so i is set to 0. Boss Kill de-schedules for 60 seconds. 60 seconds pass (960 game frames). Boss Kill is re-scheduled so i is set to 10. The result is only expected values at the cost of a loss in parallelism. Any number of such action sequences could manipulate i as long as they grab a lock on it before hand. The "Critical Section" action is implemented using a polling loop that waits a fixed amount of time (how long?) each iteration until the boolean is in the free state. When it passes the loop it will alter the boolean to the in use state to prevent other polling threads from entering such a section. When leaving the section the boolean is returned to the free state. There are no special language or native features behind how it operates so any thread crashes that cause a thread to not run the leave code will result in a deadlock. Nothing also prevents you from externally altering the boolean lock state with standard set actions breaking it as well. Since a pooling loop is run each frame while blocked there is a performance overhead unlike real operating system locks which generate scheduler events. ====Using Multithreading==== Outside of the natural multithreading provided by events firing triggers, there are a few cases you would want to purposely create new threads. This could be to avoid the operation limit or to take advantage of the dynamic memory nature of thread stacks.
Summary:
Please note that all contributions to the SC2Mapster Wiki are considered to be released under the CC BY-NC-SA
Cancel
Editing help
(opens in new window)
Follow on IG
TikTok
Join Fan Lab