国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

python調用外部子進程,通過管道實現異步標準輸入和輸出的交互

2019-11-14 17:45:57
字體:
來源:轉載
供稿:網友

我們通常會遇到這樣的需求:通過C++或其他較底層的語言實現了一個復雜的功能模塊,需要搭建一個基于Web的Demo,方法查詢數據。由于Python語言的強大和簡潔,其用來搭建Demo非常合適,Flask框架和jinja2模塊功能為python提供了方便的web開發能力。同時,python能夠很方便的同其他語言的代碼交互。因此我們選擇python作為開發Demo的工具。假設我們需要調用的模塊(提供底層服務)通過標準輸入循環讀入數據,處理完畢后把結果寫出到標出輸出,這樣的場景在linux環境下很常見,依賴于Linux強大的重定向能力。然而,非常不幸的是,底層模塊有一個很重的初始化過程,因此我們不能夠每次查詢請求都去重新生成調用底層模塊的子進程。解決方案就是只生成一次子進程,然后對每個請求通過管道(pipe)來和子進程交互。

Python的subPRocess模塊可以很容易地生成子進程,類似Linux系統調用fork和exec。subprocess模塊的Popen對象可能以非阻塞的方式調用外部可執行程序,因此我們使用Poen對象來實現需求。如果我們想要把數據寫入子進程的標準輸入stdin,那么在創建Popen對象的時候就需要指定參數stdin為subprocess.PIPE;同樣,如果我們需要從子進程的標準輸出中讀取數據,那么在創建Popen對象的時候就需要指定參數stdout為subprocess.PIPE。先看一個簡單的例子:

from subprocess import Popen, PIPEp = Popen('less', stdin=PIPE, stdout=PIPE)p.communicate('Line number %d./n' % x)

communicate函數返回一個二元組(stdoutdata, stderrdata),包含了子進程的標準輸出和標出錯誤的輸出數據。然而,由于Popen對象的communicate函數會阻塞父進程,同時還會關閉管道,因此每個Popen對象只能調用一次communicate函數,如果有多個請求必須重新生成Popen對象(重新初始化子進程),不能滿足我們的需求。

因此,我們只有往Popen對象的stdin和stdout對象里寫入和讀取數據才能實現我們的需求。然而,不幸的是subprocess模塊默認情況下只運行在子進程結束的時候讀取一次標準輸出。Both subprocess and os.popen* only allow input and output one time, and the output to be read only when the process terminates

進過一番研究之后我發現通過fcntl模塊的fcntl函數可以把子進程的標準輸出改為非阻塞的方式,從而達到我們的目的。這樣困擾我許久的問題終于得到了完美解決。代碼如下:

 1 #!/usr/bin/python                                                                                                                                                       2 # -*- coding: utf-8 -*- 3 # author:  4 from subprocess import Popen, PIPE 5 import select 6 import fcntl, os 7 import time 8  9 class Server(object):10   def __init__(self, args, server_env = None):11     if server_env:12       self.process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=server_env)13     else:14       self.process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)15     flags = fcntl.fcntl(self.process.stdout, fcntl.F_GETFL)16     fcntl.fcntl(self.process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)17 18   def send(self, data, tail = '/n'):19     self.process.stdin.write(data + tail)20     self.process.stdin.flush()21 22   def recv(self, t=.1, stderr=0):23     r = ''24     pr = self.process.stdout25     if stderr:26       pr = self.process.stdout27     while True:28       if not select.select([pr], [], [], 0)[0]:29         time.sleep(t)30         continue31       r = pr.read()32       return r.rstrip()33     return r.rstrip()34 35 if __name__ == "__main__":36   ServerArgs = ['/path/to/server', '/path/to/args']37   server = Server(ServerArgs)38   test_data = '拿鐵', '咖啡'39   for x in test_data:40     server.send(x)41     print x, server.recv()     

 另外,調用一些外部程序時,可能需要指定相應的環境變量,方式如下:

  my_env = os.environ  my_env["LD_LIBRARY_PATH"] = "/path/to/lib"  server = server.Server(cmd, my_env)

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 双城市| 邢台市| 福建省| 阿瓦提县| 永胜县| 和平县| 偏关县| 江口县| 阿克苏市| 呼伦贝尔市| 周至县| 新源县| 镇沅| 阿图什市| 开江县| 余庆县| 公安县| 浏阳市| 昌平区| 昌宁县| 赣榆县| 西宁市| 夏邑县| 恭城| 余江县| 子洲县| 台湾省| 微博| 西藏| 临泽县| 岑巩县| 南江县| 博爱县| 双牌县| 长岛县| 宁河县| 商都县| 山东省| 买车| 壶关县| 房产|