Conceptual model that combine itself most things needed to run / control / communicate processes.
Why?
It is fun but all is side effect from optimizing script that helped me in my game. It became too big and looping inside became complicated. So I split actions and put in different scripts. Nothing unusual.
BUT:
1. How to run process
2. How to set and get values between processes
3. How to terminate process
1. How to RUN process
It is simple question without simple answer. There 2 main methods to run process:
1. RUN and leave
http://lexikos.github.io/v2/docs/commands/Run.htm
2. RUN and wait RunWait
Both functions have identical parameters and last one is PID. But in both cases PID is know just when function return to caller. That mean till we waiting (RunWait) we haven`t PID at all.
Code: Select all
;Master.ahk
RunWait("ChildTask.ahk",,,PID) ;script will wait ChildTask.ahk to end
MsgBox(PID)
If anything OK this 2 lines are perfect, but what if Master.ahk ended before ChildTask.ahk ended. We must terminate ChildTask.ahk manualy (if 10 child tasks it mean terminate all 10 tasks manualy).
Code: Select all
myRun(name, wait:=1, para:="") { ;execute task
global PID
run(name " " para,,,PID)
if wait, ProcessWaitClose(PID) ;runwait not usable
}
Now we
RUN process
GET PID and if wait
WAIT task to exit. If Child freeze or waste too much time We can kill it by PID.
2. How to set and get values between processes
I use iniREAD / iniWRITE functions due to very simple but effective way. Name of INI is just script name .VAR (ChildTask.ahk.VAR).
Now tasks can read / write to all possible VAR files - that made inter-process communication.
3. How to terminate process
The easy way is:
but actually not so clever and usable. So We need mechanism to terminate child process controllable.
Code: Select all
__goExit() {
ExitApp ;terminate current process
}
OnMessage(0x10, "__goExit") ;made script persistent
Now script
CAN be invited to
EXIT by its methods and to made closing procedures. In other case if child task is terminated (manually or other ways) its own child tasks cant be invited to exit. So let init one more function:
Code: Select all
__onExit() {
;Exit code here
}
OnExit( "__onExit")
Class Task - combine all this things in small but effective way for multiprocessing:
Code: Select all
;Copyright (c) D.Donchev
class Task {
static List:= {} ;static list
__new(n, o:= 0x03, p:= 1) {
this.name:= n, this.exit:= o
if p, Task.List.push(this) ;new instance of Task add itself to list
}
set(name, valu, sect:="VAR") { ;set variable value into ini
iniWrite(valu, this.name ".VAR", sect, name)
return valu
}
get(name, sect:="VAR") { ;get variable value from ini
return iniRead(this.name ".VAR", sect, name)
}
run(wait:=1, para:="") { ;execute task
;run(this.name " " para,,,pid) ;this way parameters cannot be send to script
;probably windows specific - shortcut to script with parameters not work too
run("autohotkey.exe " this.name " " para,,,pid) ;now script will take parameters
this.PID:= pid ;take PID immediately
if wait, ProcessWaitClose(this.PID) ;runwait not usable
}
del(pos:=0) {
if this.exit & 0x01 { ;0x01 - terminate task
DetectHiddenWindows On ;see hidden processes
SetTitleMatchMode RegEx ;case insensitive i)
SplitPath(this.name, t) ;get title
WinClose("i)" t) ;case INsensitive
}
if this.exit & 0x02, FileDelete(this.name ".VAR") ;0x02 - delete task.ahk.VAR
if this.exit & 0x04, FileDelete(this.name) ;0x04 - delete task.ahk
Task.List.removeAt(pos? pos: this.ind()) ;remove task from list
this.base:="" ;clear object
}
ind() {
while this.name != Task.List[++res].name {
}
return res > Task.List.length? 0: res
}
} ;class Task
__onExit() {
while Task.List.length
Task.List[Task.List.length].del(Task.List.length)
}
__goExit() {
ExitApp ;terminate current process
}
OnMessage(0x10, "__goExit") ;made script persistent
OnExit( "__onExit")
classTask.ahk contain all parts of multi-processing.
USAGE - It is more complicated to be explained than real usage itself - so let see the examples:
Code: Select all
#SingleInstance Force
#include classTask.ahk
t1:= new Task("add.ahk") ;add task to list
t2:= new Task("sub.ahk") ;add task to list
1:: ;process 1 runWAIT
t1.set("Arg1", 1)
t1.set("Arg2", 10)
t1.run(), msgBOX("Result:=" t1.get("Result") " PID:=" t1.PID)
return
2:: ;process 2 run
t2.set("Arg1", 2)
t2.set("Arg2", 20)
t2.run(0), msgBOX("PID:=" t2.PID)
return
#!Q::ExitApp
Master.ahk - it is process that run other processes (like task manager).
Code: Select all
#SingleInstance Force
#include classTask.ahk
t:= new Task(A_ScriptName,,0)
t.set("Result",t.get("Arg1") + t.get("Arg2"))
ExitApp ;needed because of OnMessage()
add.ahk - contain (+) operator and demonstrate straight process. ExitApp is needed because OnMessage() made script persistent.
Code: Select all
#SingleInstance Force
#include classTask.ahk
t:= new Task(A_ScriptName,,0)
t.set("Result",t.get("Arg1") - t.get("Arg2"))
#!3::__goExit()
sub.ahk - contain (-) operator. Here no ExitApp and script will stay runned till user press WIN+ALT+3 or script catch WM_CLOSE.
CONCLUSIONS:
( + ) One only mechanism for "parent" and "child" processes.
( + ) Can be made chains or trees of processes.
( + ) If "parent" process will be closed it will invite all its "child" processes to be closed (kill process prevent closing invitations).
( - ) All processes became Persistent and ExitApp is needed.
( - ) Process clean start (variable values can be stored in .VAR or other way).
( - ) Relatively SLOW start.
Method void real multi-processing.
Enjoy!