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

от автора

Костыль

Доброго всем времени суток. Предлагаю костыль, реализующий механизм многопоточности (а точнее «многопроцессности») для 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, но для функционирования механизма он не обязателен).
В ходе дебага у меня накопились некоторые наблюдения и комменты, коими я поделюсь, если будет спрос — дело в том, что потребуется время, чтобы привести их в читабельный вид. Конструктивная критика приветствуется.

ссылка на оригинал статьи http://habrahabr.ru/post/189786/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *