newLISP 多工介紹
以下函式允許您與作業系統互動
- ! 在作業系統中執行命令
- abort 停止所有生成的程序
- destroy 殺死一個程序
- exec 執行一個程序並從中讀取或寫入
- fork 啟動一個 newLISP 子程序執行緒(Unix)
- pipe 建立用於程序間通訊的管道
- process 啟動一個子程序,重新對映標準 I/O 和標準錯誤
- semaphore 建立和控制訊號量
- share 與其他程序和執行緒共享記憶體
- spawn 建立一個新的 newLISP 子程序
- sync 監控和同步生成的程序
- wait-pid 等待子程序結束
由於這些命令與您的作業系統互動,您應該參考平臺特定問題的文件和限制。
! 執行系統命令並在控制檯中顯示結果。exec 函式執行類似的操作,但它會等待作業系統完成,然後將標準輸出作為字串列表返回,每行一個字串
(exec "ls -Rat /usr | grep newlisp")
;->
("newlisp" "newlisp-edit" "newlispdoc" "newlisp" "newlisp.1"
"newlispdoc.1" "/usr/share/newlisp:"
"/usr/share/newlisp/guiserver:"
"/usr/share/newlisp/modules:" "/usr/share/newlisp/util:"
"newlisp.vim" "newlisp" "/usr/share/doc/newlisp:"
"newlisp_index.html" "newlisp_manual.html"
"/usr/share/doc/newlisp/guiserver:")
像往常一樣,您將擁有所有樂趣來引用和雙重引用以將您的命令傳遞給 shell。
使用exec,您的指令碼將在命令完成之前等待,然後您的指令碼繼續執行。
(exec (string "du -s " (env "HOME") "/Desktop"))
您只會在命令完成時看到結果。
要與 newLISP 旁邊執行的另一個程序進行互動,而不是等到程序完成,請使用process。參見 程序.
到目前為止,我們評估的所有 newLISP 表示式都按順序執行,一個接一個,因此一個表示式必須完成評估,newLISP 才能開始下一個表示式。這通常沒問題。但有時您希望啟動一個表示式,然後在第一個表示式仍在評估時繼續執行另一個表示式。或者您可能希望將一個大型作業分成多個較小的作業,也許可以利用計算機擁有的任何額外的處理器。newLISP 使用三個函式進行這種多工處理:spawn 用於建立並行執行的新程序,sync 用於監控和完成它們,以及abort 用於在它們完成之前停止它們。
對於以下示例,我將使用這個簡短的“脈衝”函式
(define (pulsar ch interval)
(for (i 1 20)
(print ch)
(sleep interval))
(println "I've finished"))
當您正常執行此函式時,您會看到列印了 20 個字元,每 interval 毫秒列印一個。此函式的執行會阻塞其他所有操作,您必須等待所有 20 個字元,或者使用 Control-C 停止執行。
要在此函式與當前函式並行執行的另一個程序中執行此函式,請使用spawn 函式。提供一個符號來儲存表示式的結果,然後是將要評估的表示式
> (spawn 'r1 (pulsar "." 3000)) 2882 > .
函式返回的數字是程序 ID。現在,您可以在終端中繼續使用 newLISP,而 pulsar 繼續不斷中斷您。這非常煩人 - 點會一直出現在您的輸入中間!
再啟動幾個
> (spawn 'r2 (pulsar "-" 5000)) 2883 > (spawn 'r3 (pulsar "!" 7000)) 2884 > (spawn 'r4 (pulsar "@" 9000)) 2885
要檢視有多少個程序處於活動狀態(並且尚未完成),請不帶引數使用sync 函式
> (sync) (2885 2884 2883 2882)
如果要停止所有 pulsar 程序,請使用abort
(abort) true
在 MacOS X 上,嘗試這個更有趣的版本,它使用駐留的語音
(define (pulsar w interval)
(for (i 1 20)
(! (string " say " w))
(sleep interval))
(println "I've finished"))
(spawn 'r1 (pulsar "spaghetti" 2000))
(spawn 'r2 (pulsar "pizza" 3000))
(spawn 'r3 (pulsar "parmesan" 5000))
sync 也允許您監控當前正在執行的程序。提供以毫秒為單位的值;newLISP 會等待那個時間,然後檢查生成的程序是否已完成
> (spawn 'r1 (pulsar "." 3000)) 2888 > . > (sync 1000) nil
如果結果為 nil,則程序尚未完成。如果結果為 true,則所有程序都已完成。現在 - 並且只有在sync 函式執行並返回 true 之後 - 返回符號 r1 的值將設定為程序返回的值。對於 pulsar,這將是字串“我已完成”。
I've finished > r1 nil > (sync 1000) true > r1 "I've finished" >
請注意,程序已完成 - 或更確切地說,它列印了其結束訊息 - 但符號 r1 直到sync 函式執行並返回 true 時才被設定。這是因為sync 返回 true,而返回符號具有值,只有當所有生成的程序都已完成時。
如果您想等待所有程序完成,可以執行一個迴圈
(until (sync 1000))
每秒檢查一次,看看程序是否已完成。
作為獎勵,許多現代計算機都擁有多個處理器,並且您的指令碼可能能夠更快地執行,如果每個處理器都能將其精力集中在一個任務上。newLISP 將根據其所處硬體的任務排程和處理器排程留給作業系統來處理。
有一些用於操作程序的更底層的函式。這些函式不像上一節中描述的生成的程序技術那樣方便或易於使用,但它們提供了一些附加功能,您可能有一天會發現這些功能有用。
您可以使用fork 在另一個程序中評估表示式。一旦程序啟動,它不會將值返回給父程序,因此您必須考慮如何獲取它的結果。以下是如何在單獨的程序中計算質數並將輸出儲存到檔案的方法
(define (isprime? n)
(if (= 1 (length (factor n)))
true))
(define (find-primes l h)
(for (x l h)
(if (isprime? x)
(push x results -1)))
results)
(fork (append-file "/Users/me/primes.txt"
(string "the result is: " (find-primes 500000 600000))))
在此,由fork 啟動的新子程序知道如何查詢質數,但是,與生成的程序不同,它不能將資訊返回給其父程序以報告它找到了多少個質數。
程序可以共享資訊。share 函式設定一個公共記憶體區域,就像一個公告板,所有程序都可以讀寫。後面的章節有一個關於share 的簡單示例:參見 一個簡單的 IRC 客戶端。)
為了控制程序如何訪問共享記憶體,newLISP 提供了semaphore 函式。
如果您希望分叉的執行緒相互通訊,您將需要先做一些管道工作。使用pipe 設定通訊通道,然後安排一個執行緒監聽另一個執行緒。pipe 返回一個列表,其中包含對新程序間通訊管道的讀寫控制代碼,然後您可以將這些控制代碼用作read-line 和write-line 函式的通道來讀寫。
(define (isprime? n)
(if (= 1 (length (factor n)))
true))
(define (find-primes l h)
(for (x l h)
(if (isprime? x)
(push x results -1)))
results)
(define (generate-primes channel)
(dolist (x (find-primes 100 300))
(write-line channel (string x)))) ; write a prime
(define (report-results channel)
(do-until (> (int i) 290)
(println (setq i (read-line channel))))) ; get next prime
(define (start)
(map set '(in out) (pipe)) ; do some plumbing
(set 'generator (fork (report-results in)))
(set 'reporter (fork (generate-primes out)))
(println "they've started"))
(start)
they've started 101 103 107 109 ...
(wait-pid generator)
(wait-pid reporter)
請注意,字串“它們已啟動”出現在任何質數列印之前,即使該println 表示式出現線上程啟動之後。
wait-pid 函式等待由fork 啟動的執行緒完成 - 當然,您不必立即執行此操作。
要啟動一個與 newLISP 並行執行的新作業系統程序,請使用process。與fork 一樣,您首先需要設定一些合適的管道,以便 newLISP 可以與該程序進行對話和監聽,在以下示例中,該程序是 Unix 計算器 bc。write-buffer 函式寫入 myout 管道,該管道透過 bcin 被 bc 讀取。bc 的輸出透過 bcout 指向,並由 newLISP 使用read-line 讀取。
(map set '(bcin myout) (pipe)) ; set up the plumbing
(map set '(myin bcout) (pipe))
(process "/usr/bin/bc" bcin bcout) ; start the bc process
(set 'sum "123456789012345 * 123456789012345")
(write-buffer myout (string sum "\n"))
(set 'answer (read-line myin))
(println (string sum " = " answer))
123456789012345 * 123456789012345 = 15241578753238669120562399025
(write-buffer myout "quit\n") ; don't forget to quit!
