Πέμπτη 21 Νοεμβρίου 2024

M2000 Programming using threads

Information (27 Nov 2024, new release): Version 12 Revision 49 release at Git

This program show how we can make three or more threads with one blocking function, the KEY$ which wait for  a keypress (but internal the wait function allow threads to run).

By design when a module exit, the threads erased. Some time a thread maybe can't erased, because is in use at the time we erase them, so we use the Threads Erase statement, which erase every thread.

A thread is a part of code of a module. There are four differences from module: A thread has own stack of values (can work as FIFO or LIFO), has own Static variables, has the Interval time,  has a number and not a name.

About the no name of s thread: because we don't have a name for a thread the name space is the same as the module's which we create it. So from a thread the module's variables can be used. We can't have static variables and local variables with same name. A static variable hold the value for the next run.

From a thread we can send commands to thread master to erase it, or to change the interval, or to Hold it using This if we don't save the thread handler (the number) to a static variable. We can handle other threads if we know the thread handler.

The Thread Plan is the way the threads works. We can't change the plan if we have a running/waiting thread. The actual program , M2000 Interpreter not tun in a M2000 thread (but in a process thread), Because the program is a window program, has own event loop, and process events. Here the M2000 thread mechanism is an internal software building, with own thread loop. A Wait statement process threads/events. Also the Ask() function (the M2000 message box) also process threads/events. 

There are two plans: 

The Sequential plan execute a thread at the time interval and when the code end, the process return to thread master (the thread manager). If we use inside a thread something which wait using the thread manager then we will see other thread to run before that thread finish. We don't want to have loops inside threads because each thread is a loop by a timer which the interval set the next run. A thread maybe need more time that the interval have, so a delay inserted as needed to start the next time at the same phase.

The Concurrent plan execute one statement then switch to task manager to check if another task has to start or continue to execute. If we make block of codes using { } or they are blocks like the Repeat Until, While End While, then these blocks aren't break in parts, and executed as one statement.

Some times we want something to run from one only thread and the other just skip it. This can be done with the Part { } statement. When the block executed the control variable change state and the other threads just check the state and skip the block.

From threads we can use subs and functions without switching at that level of code, but a blocking statement  in a sub with an internal thread loop also process threads. The example have no call to user function/sub/module/simple function/lambda function/OOP members.

So this example set a thread plan randomly and start the threads.

The Sequential Plan:



The Concurrent Plan:

See above the one and one more for 21469 thread that have something between from another task. The load of threads (how many, and how fast they became active) make the thread manager (or master) to switch more times to other tasks.  The If Then Else End If also processed with interrupts (switches), because the didn't use blocks (but an If using blocks { } is different, and an if in a line without end if is like one statement too).

You can find in INFO files programs with threads: 

PEND, CONC, P, M2000_EDITOR_INFORMATION, FORM44, DEMO1, KB,  PONG, COMMON, DOWN4, CHESSGAME, PHILO, WIND4, FIRSTWINDOW, HOOKEVENT, PHONEBOOTH.

The program (just copy this in a module say A, using Edit A, press Esc to exit from editor, and write A and press enter to execute it. Remember that Esc and Ctrl+C can be used to stop the program if you make some changes and has a forever loop. Also use Save ThreadExample to save it as ThreadExample.gsb in M2000 user folder (use Win Dir$ to open explorer at user directory).


module ThreadMax {
profiler
if random(1,2)=1 then
thread.Plan Concurrent
print "thread.Plan Concurrent"
else
thread.Plan Sequential
print "thread.Plan Sequential"
end if

thread {
print "form outer module", timecount
refresh
} as z interval 500
module ThreadsExample {
boolean alfa
for i=1 to 3
m=random(1,6)*50
thread {
static st=1
part {
Threads
pen 15 {
print "wait for pressing space bar at ";k
}
' blocking function - wait a key
do
until key$=" "
pen 15 {
print "Thank you"
}
} as alfa
if st>1 then print "two": i--: thread this erase
st++
print "one", k, l
print "one more", k, l
} as f interval m
thread f execute static k=f, l=m
next
after 100 {
print "this is one time"
after 50 {
print "this is one time, too"
}
}
t=2
main.task 50 {if i<2 then exit
if t then t-- else t=2: print "main.task"
}
print "done"
}
ThreadsExample
Threads
wait 2000
Threads erase
print "Outer module done"
}
ThreadMax
Threads

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.