Python multi-thread 多线程 print 如何避免print的结果混乱
multithread如何写
这是我第一次写multithread,所以就是照着例子学,下面是我用来学的例子
来自于”Automate the boring stuff with Python”的15.6
import threading, time
print "Start of the program"
def takeANap():
time.sleep(5)
print "Wake up!"
thread1 = threading.Thread(target = takeANap)
thread1.start()
print "End of the program"
OK, 这个小程序的结果会是End of… 这一句先print出来,然后Wake up再出现,其实”Automate the boring….”这本书里对多线程就讲了两个例子,这是第一个,第二个是讲了如何向多线程传递参数,但是看完之后发现对今天的程序帮助不大,因为今天的程序有很大的一部分困难就是print造成的,那么下面就说说源程序的需求和第一次失败的尝试。
以一个多线程print的程序为例子
需求:
使用多线程,在三台不同的路由器上,show arp并输出问题:
很明显,输出会messed up, 因为线程各自有快有慢初步想到的方法
-
可以把两个的结果存到外部文件,然后有条理的读取,然后有条理的print。我认为这样是可行的,但是这样显然练习不到multithread的特征,因为multithread是关乎内存的,存到磁盘再读,感觉上有些违背了充分利用多线程的优势,况且这是一个小程序,就是纯粹显示的问题。
-
threading.Lock()
先说,这个失败了,因为print比较特殊,它自己的打印快慢依然会影响,这不是一个线程是否被执行完的问题,而是一个线程执行完了之后,它的输出打印并不够快而如何解决的问题,所以Lock()帮不上忙
Lock()的相关代码:
我依照这个链接里的例子创建了自己的thread的子类
-
这个myThread会以device类,也就是某个路由器为对象,调取printARP
-
我在print的前后分别加了acquire()用于上锁和release()用于解锁
class myThread(threading.Thread):
def __init__(self, device):
threading.Thread.__init__(self)
self.device = device
def run(self):
threadLock1 = threading.Lock()
threadLock1.acquire()
printARP(self.device)
threadLock1.release()
然后调用子类,开启各个子类实例化出来的thread, 把各个thread放进一个数组,数组分别调用 .join()
thread1 = myThread(rtr1)
thread2 = myThread(rtr2)
thread3 = myThread(srx)
thread1.start()
thread2.start()
thread3.start()
threads = []
threads.append(thread1)
threads.append(thread2)
threads.append(thread3)
for t in threads:
t.join()
print "All done"
然而并没有卵用,哪怕是你用了 .join让他们分别回归主线程,一会儿再说join()的事情。
- 最后的解决办法
- sys.stdout.write(a_string)
对,就是这个货,我从这个链接看到的,最后那个答案就是
原话如下
‘The issue is that python uses seperate opcodes for the NEWLINE printing and the printing of the object itself. The easiest solution is probably to just use an explicit sys.stdout.write with an explicit newline.’
然后下面有个评论是sys.stdout.write的用法
‘From my recent experience, this is absolutely true. I’m not exactly sure why it happens, but the printstatement (even when STDOUT is properly serialized and flushed) will output erratic newlines. You must usesys.stdout.write(s + ‘\n’) to avoid this. ‘
当然下面还有个评论是跟上面这位仁兄说依然需要lock的,但是anyway,这个sys.stdout.write(a_string)解决了我的问题
- 最后的代码是这样的
- 不使用自己定义的子类了,依然使用自带的threading.Thread()创建新thread
thread1 = threading.Thread(target=printARP, args=[rtr1])
thread2 = threading.Thread(target=printARP, args=[rtr2])
thread3 = threading.Thread(target=printARP, args=[srx])
然后剩下的都一样
- 唯一需要改动的地方
printARP这个功能模块,改成如下,把print改成sys.stdout.write() 记得引入sys
def printARP(device):
'''
print the ARP table on this device
'''
conn = ConnectHandler(**device)
#sys.stdout.flush()
arp = conn.send_command("show arp")
time.sleep(1)
outp = ("ARP table on %s :" % get_name_of_obj(device, "each_device")+"\n")
# use find_prompt() to determin the output of threads are not messed up
outp += (conn.find_prompt()+"\n")
outp += (arp+"\n")
outp += "*********************\n"
sys.stdout.write(outp)
完整的代码在这里
线程的join
差点忘了说了,这个答主的答案特别好,那个图简直绝了