我正在寻找一种方法来启动多个后台进程以与 DCL 并行运行。我想等待他们全部完成并知道他们是否都成功了,或者至少有一个失败了。这就像在 bash 中使用 & 并等待。
我阅读了 spawn/nowait 的文档,但在生成多个子进程后我看不到等待的方法。我想我可以让子进程在退出时写入文件并在睡眠循环中从父进程中检查文件?但是有更好的/内置的方法吗?
两种可靠的方法是:
如果您submit
将它们作为批处理作业,您可以使用synchronize
命令依次等待每个作业。
使用run/detach
启动进程允许您指定/mailbox
接收终止消息。
子流程略有不同。一种方法是共享一个逻辑名称表,并让每个子流程相应地更新其状态。不保证会报告异常终止。
如果您愿意做一些额外的工作,您可以创建几个小应用程序来使用锁管理器或公共事件标志集群来传达状态更新。
$ BKGVFY = F$VERIFY(0) !******Start of BACKGROUND******
$!*DOC+
$!*
$!* Procedure : BACKGROUND.COM
$!*
$!* Abstract : Invoke Commands in Background
$!*
$!* Prototype : @MYDCL:BACKGROUND "RUN PROG1" "@COMFIL" "RUN PROG2"
$!*
$!* Description: This utility is used to run background spawned jobs. It
$!* will return control after all of the jobs have run.
$!*
$!* Note: Requires GRPNAM or SYSNAM Privilege
$!*
$!* Parameters : P1 = Background Command (Required)
$!* P2 = Background Command (Optional)
$!* P3 = Background Command (Optional)
$!* P4 = Background Command (Optional)
$!* P5 = Background Command (Optional)
$!* P6 = Background Command (Optional)
$!* P7 = Background Command (Optional)
$!* P8 = Background Command (Optional)
$!*
$!* Change Control Log:
$!*
$!* Who Date Description
$!* --------------- ---------- -----------
$!* Scott Kelbell 02/16/2010 Initial version
$!*
$!*DOC-
$ !
$ ! VALIDATION
$ !
$ IF P1 .EQS. "" .OR. P1 .EQS. "HELP" .OR. P1 .EQS. "?" THEN EXIT
$ IF F$PRIVILEGE("GRPNAM")
$ THEN
$ BKGPRIV = "GROUP"
$ ELSE
$ IF F$PRIVILEGE("SYSNAM")
$ THEN
$ BKGPRIV = "SYSTEM"
$ ELSE
$ WRITE SYS$OUTPUT "%BACKGROUND-F-IPRV, insufficient priv (GRPNAM)"
$ EXIT
$ ENDIF
$ ENDIF
$ !
$ ! INITIALIZATION
$ !
$ BKGN = "BKG" + F$UNIQUE() ! Background Logical Prefix
$ BKGFIL = "SYS$LOGIN:" + BKGN ! Background File Prefix
$ BKGCNT = 0 ! Parameter Counter
$ !
$ ! MAINLINE
$ !
$ LOOP:
$ IF BKGCNT .EQ. 8 THEN GOTO LOOP_WAIT
$ BKGCNT = BKGCNT + 1
$ IF P'BKGCNT' .NES. "" ! For each parameter
$ THEN
$ BKGLOG = BKGFIL + ".LOG_" + F$STRING(BKGCNT)
$ CREATE/FDL=NL: 'BKGFIL'.TMP_'BKGCNT' ! Create the command procedure
$ OPEN/APPEND 'BKGN' 'BKGFIL'.TMP_'BKGCNT'
$ BKGLNM = BKGN + "_" + F$STRING(BKGCNT)
$ WRITE 'BKGN' "$ SET VERIFY"
$ WRITE 'BKGN' "$ ! Background Job #", P'BKGCNT', " ", F$TIME()
$ WRITE 'BKGN' "$ SET NOON"
$ WRITE 'BKGN' "$ DEFINE/NOLOG/", BKGPRIV, " ", BKGLNM, " RUNNING"
$ WRITE 'BKGN' "$ ", P'BKGCNT'
$ WRITE 'BKGN' "$ DEFINE/NOLOG/", BKGPRIV, " ", BKGLNM, " FINISHED"
$ WRITE 'BKGN' "$ EXIT ! End of Background Job"
$ CLOSE 'BKGN'
$ DEFINE/'BKGPRIV' 'BKGN'_'BKGCNT' INITIALIZED
$ SPAWN/NOWAIT/OUTPUT='BKGLOG'/NOLOG @'BKGFIL'.TMP_'BKGCNT'
$ GOTO LOOP
$ ENDIF
$ BKGCNT = BKGCNT - 1
$ !
$ ! WAIT LOGIC
$ !
$ LOOP_WAIT:
$ CURLOG = BKGN + "_" + F$STRING(BKGCNT)
$ IF F$TRNLNM("''CURLOG'") .NES. "FINISHED"
$ THEN
$ WAIT 00:00:05
$ GOTO LOOP_WAIT
$ ENDIF
$ DEASSIGN/'BKGPRIV' 'BKGN'_'BKGCNT'
$ DELETE/NOLOG/NOCONFIRM 'BKGFIL'.TMP_'BKGCNT';*
$ BKGCNT = BKGCNT - 1
$ IF BKGCNT .GT. 0 THEN GOTO LOOP_WAIT
$ !
$ ! EXIT LOGIC
$ !
$ EXIT:
$ BKGVFY = F$VERIFY(BKGVFY) !******End of BACKGROUND******
您总是可以在 Perl 中进行管道打开,然后使用 waitpid 等待它:
$ perl -e "my $pid = open F, '| wait 00:00:05'; print qq/waiting for $pid\n/; waitpid($pid, 0); close F;"
waiting for 6233
将您要等待的实际 DCL 命令替换为“wait 00:00:05”命令。这里的文件句柄 F 是一个只写文件句柄,连接到孩子的标准输入/SYS$INPUT。如果子进程对 SYS$INPUT 进行任何阻塞读取,父进程将需要写入它所期望的任何内容,否则子进程将挂起。或者你可以把管道字符 | 在命令的末尾,然后 F 将是一个连接到孩子的 stdout/SYS$OUTPUT 的只读文件句柄。如果等待多个孩子,您可能需要使用 wait() 而不是 waitpid()。
只需轮询 suprocess 计数。f$getjpi 的“PRCCNT”参数是您需要的值,名称可能会产生误导(作为总进程作业数),它是子进程数。那是一个“oneliner”(当然,“按 kB 支付”可能会将其拆分为半页):
$ spawn/nowait command1
$ spawn/nowait command2
...
$waitall: if f$getjpi("","PRCCNT").gt.0 then pipe wait 0::.5 ; goto waitall
...
将轮询间隔设置为合理的值,适合命令执行时间,当然,0.5 秒不是通用的。
正如所指出的,SYNCHRONIZE 是最稳健的方法。
一种草率但通常足够的方法是首先使用 spawn/nowait 创建寿命最短的任务,然后 spawn/wait 预期最后运行的任务。
为了加强这一点,您可以使用 F$CONTEXT 进入轮询循环,查看 MASTER_PID = your-pid 计算返回的 F$PID 值的数量,以及可选的 F$GETJPI
Hackish 方法是例如尝试重用进程名称,或尝试打开不允许共享的进程日志文件,该文件仅在进程消失后才有效,或解析 SHOW PROC/SUB 输出。
也许您可以创建一个简单的 shell 程序来执行一堆 (lib$)spawns 并使用完成 AST 来确定所有完成的时间?
您可能还想查看 CRTL 函数 waitpid。
嗯,海因。