Pull to refresh

«Многопоточность» WSH VBScript

Reading time 18 min
Views 10K

Костыль


Доброго всем времени суток. Предлагаю костыль, реализующий механизм многопоточности (а точнее «многопроцессности») для WSH VBScript. Не рекомендуется лицам с аллергией на индокод.
Первая мысль о потенциальных удобствах многопоточности пришла однажды во время решения на добровольных началах относительно несложной задачи по обработке и визуализации информации. И, поскольку установка IDE навроде Visual Studio и наш Helpdesk — понятия несовместимые, из «легальных» оставались только VBA и WSH. В то время был выбран последний. А действующие административные ограничения и желание получить дружественные методы продиктовали требования к данной реализации многопроцессности:
  • Код расположен в одном файле, как обычный сценарий.
  • Использует минимум ActiveX, и только встроенных, работоспособен без WMI, под WSH x86/x64.
  • Достаточная функциональность и удобство важнее быстродействия и паттернов.

Особенности:
  • Запущенный скрипт выполняет служебную функцию. Основной код выполняется в последующих процессах.
  • В «полной версии» основной код размещается только в классах, экземпляр любого из которых можно создать в отдельном процессе. В каждом новом процессе скрипт создает экземпляр служебного класса, который создает экземпляр указанного целевого класса, вызывает целевой метод, контролирует его ход, создает в глобальном контексте скриптов переменные, ссылающиеся на созданные экземпляры классов. В предшествующий скрипт возвращается объект инициализированного целевого класса, и «обрабатываются» события инициализации и завершения целевого класса запущенного скрипта.
  • В «легкой версии» логика аналогична, только основной код размещается внутри sub’ов, предшествующему скрипту возвращается не объект, а индекс запущенного, и нет обработки событий.
  • Для создания нового процесса скрипт рекурсивно запускает самого себя, передав данные о вызываемом методе и идентифицирующую информацию в именованном аргументе.
  • Единого пространства исполнения нет, каждый скрипт хранит свои данные и объекты в своем контексте. Для обмена данными объект Me первого скрипта передается в последующие с применением GlobalContainer, описанным в теме «обмен данными и объектами между скриптами — 2» на Сером форуме. Каждый скрипт после завершения целевого метода ожидает разрешения на завершение, такая задержка позволяет забрать из него полученную информацию.
  • Функционирует одинаково в WSH WScript и CScript, и в виде скомпилированного в ScriptCryptor exe-файла (правда, в exe не работают методы class_terminate).

Минусы:
  • Сложность в отладке из-за использования во многих методах on error resume next и execute. Все ж таки это костыль.
  • Громоздкость.
  • Порядок завершения скриптов полностью в ответственности разработчика. Естественно, при обращении к объектам завершенного скрипта возникнет ошибка.

Актуально:
  • Не реализованы мьютексы и механизмы залочивания метода от использования более чем одним процессом.
  • Возможны сбои при одновременном доступе к переменным скрипта из контекстов других.
  • Окно проводника остается в памяти при закрытии окна консоли CScript.exe.


Легкая версия mproclite.vbs:


option explicit
launch "base"

' main programm section

sub base()
	startproc "msg"
	startproc "msg"
	startproc "msg"
	msgbox "base, id = " & id, 64
	free id
end sub

sub msg()
	msgbox "msg, id = " & id, 64
	free id
end sub

' do not modify service section

sub launch(byval destination)
	dim job
	executeglobal "dim scene, container, signature, subname, jobs, id, state, release"
	release = false
	if not wscript.arguments.named.exists("task") then
		dim elt
		executeglobal "dim found, lost"
		id = 0
		found = 0
		lost = 0
		signature = ""
		randomize
		do
			signature = signature & hex(rnd * 16)
		loop while len(signature) < 16
		set scene = me
		set jobs = createobject("Scripting.Dictionary")
		set jobs(0) = scene
		set container = getobject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
		container.putproperty signature, scene
		startproc destination
		on error resume next
		do until (lost >= found) or release
			for elt = found to 1 step -1
				if typename(jobs(elt)) = "Object" then
					lost = lost + 1
					jobs(elt) = empty
				end if
				err.clear
				wscript.sleep 1
			next
		loop
		release = true
		executeglobal "scene_beforeterminate"
		for elt = found to 1 step -1
			if typename(jobs(elt)) = "VBScriptTypeInfo" then
				jobs(elt).wscript.timeout = 1
				jobs(elt).wscript.quit
				err.clear
				nojobs = false
			end if
			wscript.sleep 1
		next
		container.quit
	else
		job = split(wscript.arguments.named("task"), ";")
		signature = cstr(job(0))
		id = clng(job(1))
		subname = cstr(job(2))
		do
			for each container in createobject("Shell.Application").windows
				if isobject(container.getproperty(signature)) then
					exit do
				end if
			next
			wscript.sleep 1
		loop
		set scene = container.getproperty(signature)
		set jobs = scene.jobs
		state = 4
		set jobs(id) = me
		executeglobal subname
		state = 24
		do until release
			wscript.sleep 10
		loop
		state = 28
	end if
end sub

function startproc(subname)
	startproc = createproc(subname)
	joint startproc, 4, 0
	REM do while getstate(startproc) < 4
		REM wscript.sleep 10
	REM loop
end function

function createproc(subname)
	if me is scene then
		if not release then
			found = found + 1
			createproc = found
			set jobs(createproc) = nothing
			createobject("WScript.Shell").exec("""" & wscript.fullname & """ """ & wscript.scriptfullname & """ ""/task:" & join(array(signature, createproc, subname), ";") & """")
		end if
	else
		createproc = scene.createproc(subname)
	end if
end function

function getjob(target)
	on error resume next
	if jobs.exists(target) then
		set getjob = jobs(target)
		if err.number = 0 then exit function
		err.clear
	end if
	set getjob = nothing
end function

sub share(varname, value)
	scene.newvar varname
	if isobject(value) then
		execute "set scene." & varname & " = value"
	else
		execute "scene." & varname & " = value"
	end if
end sub

sub newvar(varname)
	executecommand "dim " & varname
end sub

sub executecommand(command)
	executeglobal command
end sub

function getstate(target)
	dim elt
	if jobs.exists(target) then
		on error resume next
		set elt = jobs(target)
		getstate = elt.state
		if err.number <> 0 then
			if not(elt is nothing) then
				getstate = 64
			else
				getstate = 1
			end if
		end if
		set elt = nothing
	else
		getstate = 64
	end if
end function

function isresponsive(target)
	isresponsive = cbool(getstate(target) and 28)
end function

sub free(target)
	if jobs.exists(target) then
		on error resume next
		jobs(target).release = true
	else
		dim elt, subname
		for elt = scene.found to 1 step -1
			on error resume next
			subname = jobs(elt).subname
			if subname = target then
				free jobs(elt).id
			end if
			err.clear
		next
	end if
end sub

function joint(target, state, timeout)
	dim reftime
	reftime = timer
	on error resume next
	if jobs.exists(target) then
		if isnumeric(target) then
			do while getstate(target) < state
				if timeisout(timeout, reftime) then
					joint = false
					exit function
				end if
				wscript.sleep 10
			loop
		else
			dim elt, subname
			for elt = scene.found to 1 step -1
				subname = jobs(elt).subname
				err.clear
				if subname = target then
					do while getstate(target) < state
						if timeisout(timeout, reftime) then
							joint = false
							exit function
						end if
						wscript.sleep 10
					loop
				end if
				err.clear
			next
		end if
	end if
	joint = true
end function

function timeisout(timeout, reftime)
	if timeout > 0 then
		dim delta
		delta = timer - reftime
		if delta < 0 then delta = delta + 86400
		if delta > timeout then
			timeisout = true
		end if
	else
		timeisout = false
	end if
end function

sub interrupt(target, timeout)
	if jobs.exists(target) then
		on error resume next
		jobs(target).wscript.timeout = timeout
		jobs(target).wscript.quit
	else
		dim elt, subname
		for elt = scene.found to 1 step -1
			on error resume next
			subname = jobs(elt).subname
			if subname = target then
				interrupt jobs(elt).id
			end if
			err.clear
		next
	end if
end sub

sub push(name, value)
	container.putproperty name, value
end sub

function pop(name)
	on error resume next
	if isobject(container.getproperty(name)) then
		set pop = container.getproperty(name)
	else
		pop = container.getproperty(name)
	end if
end function


Описание:


Функции механизма многопроцессности легкой версии:

launch subname
Запускает служебный алгоритм, выполнение скрипта всегда начинается с его вызова.
subname – строка, указывает целевой sub, который будет выполнен в новом процессе.
Аргументы этого метода используются только в первично запущенном скрипте для начала выполнения основного кода.
В каждом новом скрипте:
  • Объявляются переменные в глобальном пространстве запущенного скрипта:
    scene – ссылка на объект Me первого скрипта,
    container – ссылка на окно проводника GlobalContainer,
    signature – строка, сгенерирована для идентификации GlobalContainer,
    found, lost – только для scene – счетчики созданных и завершенных процессов скриптов,
    subname – строка, содержит цель скрипта,
    jobs – ссылка на словарь с объектами Me запущенных скриптов, созданный в scene,
    id – порядковый номер данного скрипта — ключ в словаре,
    release = Ложь — после завершения целевого sub’а скрипт будет завершен после присвоения Истина,
    state = 4 – состояние, в котором находится данный скрипт.
  • Добавляется элемент в словарь jobs.

startproc(subname)
subname – см. launch().
Создает новый процесс скрипта, ожидает его готовности до состояния 4 (см. getstate()), когда можно обращаться к объекту Me скрипта. Возвращает число — id запущенного скрипта.

createproc(subname)
subname – см. launch().
Создает новый процесс скрипта, не ожидая, возвращает число — id запущенного скрипта. Используется для асинхронного создания нескольких процессов в цикле, без ожидания готовности каждого. Заметно быстрее по сравнению с использованием startproc() для такого применения.

getjob(target)
target – число, id скрипта или строка, имя subname группы созданных скриптов.
Обеспечивает доступ к объекту Me скрипта. Возвращает ссылку на объект Me скрипта, если id не найден или скрипт завершен – Nothing.

getstate(target)
target – число, id скрипта.
Определяет состояние скрипта. Возвращает число, этап выполнения:
1 процесс создан (new process exec),
4 скрипт запущен (initialized),
24 целевой sub выполнен (sub completed),
28 скрипт освобожден (released),
64 не найден (host not found), скрипт завершен (terminated).

isresponsive(target)
target — см. getstate().
Определяет доступность объекта Me скрипта (состояния с 4 по 28). Возвращает булево значение.

executecommand command
command — строка, содержащая инструкции.
Вызов интерпретатора для выполнения операторов в глобальном пространстве скрипта.

share varname, value
varname — строка, содержащая имя переменной, value – любое значение.
Объявляет в глобальном пространстве первого скрипта переменную с именем varname, которая становится доступна всем скриптам в виде свойства scene, присваивает переменной содержимое value.

newvar varname
varname — строка, содержащая имя переменной.
Объявляет новую переменную в глобальном пространстве скрипта.

free target
target – число, id скрипта или строка, имя subname группы созданных скриптов. Разрешает завершение скрипта после выполнения целевого sub’а. Работает с одним скриптом или с группой.

joint(target, state, timeout)
target — см. free(), state — см. getstate(), timeout – число, в секундах, с миллисекундами.
Ожидает наступления состояния скрипта state, для группы скриптов ожидание длится, пока каждый не достигнет state. Ожидание ограничено таймаутом, timeout = 0 означает неограниченное ожидание. Возвращает булево значение, Истина – ожидание закончено, Ложь – таймаут. Предназначен для синхронизации работы скриптов. Например, если необходимо дождаться запуска скрипта — 4, полного завершения скрипта — 64;

interrupt target, timeout
target — см. free(), timeout – значение для wscript.timeout, в секундах.
Переводит скрипт к штатному завершению, с выполнением методов class_terminate. Если в скрипте были открыты диалоговые окна, он перейдет к завершению только после паузы timeout. Повторное появление диалогового окна в методах class_terminate остановит завершение.

push name, value
name – строка, имя свойства, value – любое значение.
Помещает в свойство GlobalContainer с именем name содержимое value.

pop(name)
name – строка, имя свойства.
Возвращает из GlobalContainer содержимое свойства с именем name.

Полная версия mproc.vbs:


option explicit
dim mproc
set mproc = new multiprocess
mproc.launch "base", "run", ""

' main programm section

class base
	
	public sub run()
		host.startproc "msg", "run", "first"
		host.startproc "msg", "run", "second"
		host.startproc "msg", "run", "third"
		msgbox "base, id = " & host.id, 64
		host.free host.id
	end sub
	
end class

class msg
	
	public sub run()
		msgbox host.aliasname & ", id = " & host.id, 64
		host.free host.id
	end sub
	
end class

' do not modify service class section

class multiprocess
	
	public primary, ancestor, parent, process, err
	public names, execs, hosts
	public id, aid, isprimary
	public classname, methodname, aliasname
	public found, lost, active
	public state, permit, release
	private container, signature, wshshell
	
	public sub launch(startclassname, startmethodname, startaliasname)
		permit = false
		release = false
		executeglobal "dim scene, host, ancestor, process"
		if not isempty(host) then exit sub
		set host = me
		executeglobal "set host.err = err"
		executeglobal "function getroot: set getroot = me: end function"
		set parent = getroot
		isprimary = not wscript.arguments.named.exists("task")
		if isprimary then
			dim sample
			state = 24
			randomize
			signature = ""
			do
				signature = signature & hex(rnd * 16)
			loop while len(signature) < 16
			aid = empty
			id = 0
			found = 0
			lost = 0
			set wshshell = createobject("WScript.Shell")
			set primary = host
			set ancestor = nothing
			set process = nothing
			set scene = parent
			set parent.ancestor = nothing
			set parent.process = nothing
			set hosts = createobject("Scripting.Dictionary")
			set execs = createobject("Scripting.Dictionary")
			set names = createobject("Scripting.Dictionary")
			classname = empty
			methodname = empty
			aliasname = empty
			set hosts(0) = host
			set container = getobject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
			container.putproperty signature, parent
			startproc startclassname, startmethodname, startaliasname
			on error resume next
			do
				for each sample in execs.keys
					if release or active = 0 then exit do
					if not (execs(sample) is nothing) then
						if execs(sample).status > 0 then
							abolish sample
						end if
					end if
					wscript.sleep 1
				next
			loop
			release = true
			state = 28
			scenequit
		else
			dim job
			job = split(wscript.arguments.named("task"), ";")
			signature = cstr(job(0))
			do
				for each container in createobject("Shell.Application").windows
					if isobject(container.getproperty(signature)) then
						exit do
					end if
				next
				wscript.sleep 1
			loop
			aid = clng(job(1))
			id = clng(job(2))
			found = null
			lost = null
			set scene = container.getproperty(signature)
			set primary = scene.host
			set hosts = primary.hosts
			set ancestor = hosts(aid)
			if isresponsive(aid) then
				set parent.ancestor = ancestor.parent.process
			else
				set parent.ancestor = nothing
			end if
			classname = cstr(job(3))
			methodname = cstr(job(4))
			aliasname = cstr(job(5))
			state = 4
			primary.implicate id, aliasname, host
			executeglobal "set process = new " & classname
			executeglobal "set host.process = process"
			executeglobal "set scene." & aliasname & " = process"
			if isresponsive(aid) then
				executeglobal "set host.ancestor.parent." & aliasname & " = process"
			end if
			state = 8
			primary.staff host
			ancestorevent "oninitialized"
			state = 12
			if methodname <> "" then
				do until permit
					wscript.sleep 10
				loop
				state = 16
				executeglobal "process." & methodname
			end if
			state = 20
			ancestorevent "oncompleted"
			state = 24
			do until release
				wscript.sleep 10
			loop
			state = 28
		end if
	end sub
	
	public default function startproc(classname, methodname, aliasname)
		set startproc = start(createproc(classname, methodname, aliasname))
	end function
	
	public function createproc(classname, methodname, aliasname)
		if aliasname = "" then aliasname = classname
		newvar aliasname
		scene.host.newvar aliasname
		createproc = primary.spawn(id, classname, methodname, aliasname)
	end function
	
	public function spawn(issuer, classname, methodname, aliasname)
		if not release then
			found = found + 1
			spawn = found
			active = found - lost
			names(spawn) = aliasname
			set hosts(spawn) = nothing
			if not hosts.exists(aliasname) then
				hosts.add aliasname, createobject("Scripting.Dictionary")
			end if
			set hosts(aliasname)(spawn) = nothing
			execs.add spawn, wshshell.exec("""" & wscript.fullname & """ """ & wscript.scriptfullname & """ ""/task:" & join(array(signature, issuer, spawn, classname, methodname, aliasname), ";") & """")
		end if
	end function
	
	public function start(target)
		select case outline(target)
		case "Nothing", "multiprocess"
			do while getstate(target) < 12
				wscript.sleep 10
			loop
			if isresponsive(target) then
				set start = hosts(target).process
				hosts(target).permit = true
			else
				set start = nothing
			end if
		case "Dictionary"
			dim elt
			set start = hosts(target)
			for each elt in start.keys
				do while getstate(elt) < 12
					wscript.sleep 10
				loop
			next
			for each elt in start.keys
				if isresponsive(elt) then
					hosts(elt).permit = true
				end if
			next
		case else
			set start = nothing
		end select
	end function
	
	public sub implicate(id, aliasname, host)
		set hosts(aliasname)(id) = host
		set hosts(id) = host
	end sub
	
	public sub staff(host)
		set hosts(host.process) = host
	end sub
	
	public sub abolish(id)
		if hosts.exists(names(id)) then
			hosts(names(id))(id) = empty
		end if
		names(id) = empty
		if isresponsive(id) then
			hosts(hosts(id).process) = empty
		end if
		hosts(id) = empty
		set execs(id) = nothing
		lost = lost + 1
		active = found - lost
	end sub
	
	private sub ancestorevent(eventname)
		if aid > 0 then
			on error resume next
			executeglobal "ancestor." & aliasname & "_" & eventname & " host.hosts(" & id & ")"
			if err.number = 424 or err.number = 438 then err.clear
		end if
	end sub
	
	public sub assignhandler(handlername, byval varsqty)
		dim vars
		vars = ""
		if varsqty > 0 then
			do
				vars = vars & "param" & varsqty
				varsqty = varsqty - 1
				if varsqty = 0 then exit do
				vars = vars & ", "
			loop
		end if
		executeglobal "sub " & handlername & "(" & vars & "): process." & handlername & " " & vars & ": end sub"
	end sub
	
	public sub newvar(varname)
		executecommand "dim " & varname
	end sub
	
	public sub executecommand(command)
		executeglobal command
	end sub
	
	public function getstate(target)
		select case outline(target)
		case "multiprocess"
			on error resume next
			getstate = hosts(target).state
			if err.number <> 0 then
				err.clear
				getstate = 64
			end if
		case "Nothing"
			getstate = 1
		case "Dictionary"
			getstate = null
		case empty
			getstate = 0
		case else
			getstate = 64
		end select
	end function
	
	private function outline(target)
		on error resume next
		if hosts.exists(target) then
			outline = typename(hosts(target))
			if err.number <> 0 then
				err.clear
				outline = "Object"
			end if
		else
			outline = empty
		end if
	end function
	
	public function isresponsive(target)
		isresponsive = cbool(getstate(target) and 28)
	end function
	
	public function getid(target)
		on error resume next
		if isobject(target) then
			if isresponsive(target) then
				getid = hosts(target).id
				if err.number = 0 then exit function
				err.clear
			end if
		elseif primary.execs.exists(target) then
			getid = target
			exit function
		end if
		getid = null
	end function
	
	public function gethost(target)
		on error resume next
		if hosts.exists(target) then
			set gethost = hosts(target)
			if err.number = 0 then exit function
			err.clear
		end if
		set gethost = nothing
	end function
	
	public sub free(target)
		select case outline(target)
		case "multiprocess"
			on error resume next
			gethost(target).release = true
			err.clear
		case "Dictionary"
			dim elt
			for each elt in gethost(target)
				free(elt)
			next
		end select
	end sub
	
	public function joint(target, state, timeout)
		dim reftime
		reftime = timer
		select case outline(target)
		case "multiprocess", "Nothing"
			do while getstate(target) < state
				if timeisout(timeout, reftime) then
					joint = false
					exit function
				end if
				wscript.sleep 10
			loop
		case "Dictionary"
			dim elt
			for each elt in gethost(target)
				do while getstate(elt) < state
					if timeisout(timeout, reftime) then
						joint = false
						exit function
					end if
					wscript.sleep 10
				loop
			next
		end select
		joint = true
	end function
	
	private function timeisout(timeout, reftime)
		if timeout > 0 then
			dim delta
			delta = timer - reftime
			if delta < 0 then delta = delta + 86400
			if delta > timeout then
				timeisout = true
			end if
		else
			timeisout = false
		end if
	end function
	
	public sub interrupt(target, timeout)
		select case outline(target)
		case "multiprocess"
			on error resume next
			with gethost(target).parent
				.wscript.timeout = timeout
				.wscript.quit
			end with
			err.clear
		case "Dictionary"
			dim elt
			for each elt in gethost(target)
				interrupt elt, timeout
			next
		end select
	end sub
	
	public sub kickout(target)
		if primary.execs.exists(target) then
			if getstate(target) < 64 then
				on error resume next
				primary.execs(target).terminate
				err.clear
			end if
		else
			select case outline(target)
			case "multiprocess"
				kickout getid(target)
			case "Dictionary"
				dim elt
				for each elt in gethost(target)
					kickout(elt)
				next
			end select
		end if
	end sub
	
	public sub terminate(target)
		interrupt target, 1
		if not joint(target, 64, 2) then kickout target
	end sub
	
	public sub push(name, value)
		container.putproperty name, value
	end sub
	
	public function pop(name)
		on error resume next
		if isobject(container.getproperty(name)) then
			set pop = container.getproperty(name)
		else
			pop = container.getproperty(name)
		end if
	end function
	
	private sub scenequit
		if isprimary then
			dim col, i, status
			col = execs.keys
			for i = ubound(col) to 0 step -1
				interrupt col(i), 1
			next
			wscript.sleep 2000
			on error resume next
			for i = ubound(col) to 0 step -1
				status = execs(col(i)).status
				if err.number = 0 and status = 0 then execs(col(i)).terminate
				err.clear
			next
			container.quit
		end if
	end sub
	
	private sub class_terminate()
		if state < 28 and isprimary then scenequit
	end sub
	
end class


Описание:


Методы класса multiprocess:

launch classname, methodname, aliasname
Запускает служебный алгоритм, выполнение скрипта всегда начинается с его вызова.
classname, methodname, aliasname – строки. Указывают целевой класс classname и метод этого класса methodname, который будет выполнен в новом процессе, экземпляр класса classname будет помещен в переменную с именем aliasname (или classname, если aliasname – пустая строка).
Аргументы этого метода используются только в первично запущенном скрипте для начала выполнения основного кода.
В каждом новом скрипте:
  • Объявляются переменные в глобальном пространстве запущенного скрипта:
    scene – ссылка на объект Me первого скрипта,
    host – экземпляр класса multiprocess данного скрипта,
    process – экземпляр целевого класса с именем classname, которое было указано предшествующим скриптом при создании процесса данного скрипта,
    ancestor – экземпляр целевого класса предшествующего скрипта.
  • Объявляются переменные в глобальном пространстве scene и предшествующего скрипта, с именем aliasname – ссылки на экземпляр целевого класса данного скрипта.
  • Задаются свойства host:
    primary – ссылка на экземпляр класса multiprocess первого скрипта,
    ancestor – ссылка на экземпляр класса multiprocess предшествующего скрипта,
    parent – объект Me данного скрипта,
    process – ссылка на process в глобальном пространстве,
    err – ссылка на объект err данного скрипта,
    names – ссылка на словарь с aliasname, созданный в primary,
    execs – ссылка на словарь с объектами wshexec запущенных процессов, созданный в primary,
    hosts – ссылка на словарь с объектами host запущенных процессов, созданный primary,
    id – порядковый номер данного скрипта — ключ в словарях,
    aid – то же, предшествующего скрипта,
    isprimary – булево, является ли данный скрипт первично запущенным,
    found, lost, active – только для primary – счетчики созданных, завершенных и активных процессов скриптов,
    classname, methodname, aliasname – содержат цель скрипта,
    permit = Ложь — целевой метод будет запущен после присвоения Истина,
    release = Ложь — после завершения целевого метода скрипт будет завершен после присвоения Истина,
    state = 4 – состояние, в котором находится данный скрипт.
  • Добавляются элементы в словари names, execs, hosts.

startproc(classname, methodname, aliasname)
classname, methodname, aliasname – см. launch().
Создает новый процесс скрипта, ожидает его готовности до состояния 12 (см. getstate()), запускает целевой метод. Возвращает ссылку на инициализированный в новом процессе экземпляр целевого класса.

createproc(classname, methodname, aliasname)
classname, methodname, aliasname – см. launch().
Создает новый процесс скрипта, не ожидая, возвращает его id. Используется для асинхронного создания нескольких процессов в цикле, без ожидания готовности каждого. Заметно быстрее по сравнению с использованием startproc() для такого применения.

start(target)
target – число, id скрипта, или строка, имя aliasname группы созданных скриптов.
Ожидает готовности скрипта, созданного с использованием createproc(), до состояния 12, разрешает выполнение целевого метода. Возможно использование для группы скриптов, имеющих одинаковый aliasname. Для одного скрипта возвращает ссылку на его инициализированный в новом процессе экземпляр целевого класса, для группы скриптов возвращает ссылку на субсловарь, содержащий все host с данными aliasname.

gethost(target)
target – число, id скрипта или строка, имя aliasname группы созданных скриптов, или объект process скрипта.
Обеспечивает доступ к экземпляру host класса multiprocess требуемого скрипта. Для одного скрипта возвращает ссылку на его host, для группы скриптов возвращает ссылку на субсловарь, содержащий все host с данными aliasname, если id не найден или скрипт завершен – Nothing.

getid(target)
target – число, id скрипта, или объект process скрипта.
Возвращает id срипта, определенное по объекту process. Только для действующих скриптов.

getstate(target)
target — см. getid().
Определяет состояние скрипта. Возвращает число, этап выполнения:
0 не найден (host not found),
1 процесс создан (new process exec),
4 host инициализирован (host initialized),
8 целевой класс инициализирован (process initialized),
12 целевой класс инициализирован, событие обработано (process initialized handled),
16 целевой метод запущен (process method launched),
20 целевой метод выполнен (process completed),
24 целевой метод выполнен, событие обработано (process completed handled),
28 скрипт освобожден (released),
64 скрипт завершен (terminated).

isresponsive(target)
target — см. getid().
Определяет доступность объекта host скрипта (состояния с 4 по 28). Возвращает булево значение.

assignhandler handlername, varsqty
handlername — строка, имя события, varsqty — число, количество передаваемых аргументов.
Создает в глобальном пространстве хэндлер события sub с именем события handlername, связывет его с одноименным методом в созданном объекте process. При наступлении события, хэндлер перенаправит вызов в process. handlername().

executecommand command
command — строка, содержащая инструкции.
Вызов интерпретатора для выполнения операторов в глобальном пространстве скрипта.

newvar varname
varname — строка, содержащая имя переменной.
Объявляет новую переменную в глобальном пространстве скрипта.

free target
target — см. gethost().
Разрешает завершение скрипта после выполнения целевого метода. Работает с одним скриптом или с группой.

joint(target, state, timeout)
target — см. gethost(), state — см. getstate(), timeout – число, в секундах, с миллисекундами.
Ожидает наступления состояния скрипта state, для группы скриптов ожидание длится, пока каждый не достигнет state. Ожидание ограничено таймаутом, timeout = 0 означает неограниченное ожидание. Возвращает булево значение, Истина – ожидание закончено, Ложь – таймаут. Предназначен для синхронизации работы скриптов. Например, если необходимо дождаться создания объекта process — 8, полного завершения скрипта — 64;

interrupt target, timeout
target — см. gethost(), timeout – значение для wscript.timeout, в секундах.
Переводит скрипт к штатному завершению, с выполнением методов class_terminate. Если в скрипте были открыты диалоговые окна, он перейдет к завершению только после паузы timeout. Повторное появление диалогового окна в методах class_terminate остановит завершение.

kickout target
target — см. gethost().
Завершает процесс скрипта на уровне ОС, используя wshexec.terminate. Возможно длительное выполнение, до 2 сек для каждого скрипта. Работает с одним скриптом или с группой.

terminate target
target — см. gethost().
Завершает скрипт, использует сначала interrupt, затем при необходимости kickout.

push name, value
name – строка, имя свойства, value – любое значение.
Помещает в свойство GlobalContainer с именем name содержимое value.

pop(name)
name – строка, имя свойства.
Возвращает из GlobalContainer содержимое свойства с именем name.

Методы, которые можно разместить в целевом классе данного скрипта в качестве хэндлеров событий инициализации целевого класса и выполнения целевого метода запущенного скрипта:

<aliasname>_ oninitialized(source)
source – передаваемый в метод объект host скрипта, вызвавшего метод, его aliasname содержится в имени метода. Метод вызывается после инициализации целевого класса запущенного скрипта (state = 8).

<aliasname>_ oncompleted(source)
source — передаваемый в метод объект host скрипта, вызвавшего метод, его aliasname содержится в имени метода. Метод вызывается после выполнения целевого метода запущенного скрипта (state = 20).

Пример:


Для mproclite и mproc демонстрируется работа на примере абстрактной задачи: для каждого символа из строки letters создаются отдельные процессы, каждый в цикле помещает свой символ в буфер, по мере заполнения которого другой процесс выводит по 3 «слова» в консоль. Для WScript консоль сымитирована окном IE. Попутно выводятся запущенные и остановленные процессы ОС (в примере используется WMI, но для функционирования механизма он не обязателен).
В ходе дебага у меня накопились некоторые наблюдения и комменты, коими я поделюсь, если будет спрос — дело в том, что потребуется время, чтобы привести их в читабельный вид. Конструктивная критика приветствуется.
Tags:
Hubs:
+6
Comments 2
Comments Comments 2

Articles