关注公众号“码农帮派”,查看更多系列技术文章:
一.进程
1.进程的运行环境
每个运行的进程,系统都会分配一个相关的运行环境,一般的可以将该运行环境认为是进程环境变量的集合,当进程启动的时候,环境变量就确定了,只有当前进程才能够修改其环境变量。Python的os模块中提供了environ属性,来记录当前进程的运行环境,environ是字典数据结构,以key-value的方式存储环境变量(key是环境变量的变量名,一般要求字母全部大写),value是对应的环境变量的值:
import os
path = os.environ.get('PATH')
print path
获得进程所有的环境变量:
import os
for key in os.environ.keys():
print key, os.environ[key]
2.创建进程
2.1使用os模块创建进程
os模块中用来创建进程的函数有system和exec家族函数,使用system创建进程:
import os
os.system('ls')
上面的程序在当前进程中开启了一个新的进程,在相当于在命令行中执行ls操作(Mac和Linux中的ls,Windows中的dir命令)。
在开发中,有时需要运行其他的进程来处理一些事务,e.g.在Windows上运行一个exe文件转换图片的sRGB格式:
import os
# magick.exe是windows7上转换图片sRGB格式的工具
commandTool = os.getcwdu()+os.sep+'magick.exe'
# 处理tmp文件夹中的图片
def convertsRGBImageFromTmp(pngPath):
command = "{0} {1} {2}".format(commandTool, pngPath, pngPath)
statCode = os.system(command)
【说明】
在cocos2d/wxPython开发过程中,由于win7对图片格式审核升级的原因,有时会有sRGB警告的弹窗,此时可以使用magick.exe工具进行格式转换,上面的代码使用了os.system(command)开启新进程来处理。
exec家族函数同样可以开启新进程,下面使用execl开启新的进程运行textExec.py文件中的代码:
import os
os.execl("testExec.py", '1', '2')
testExec.py文件:
#coding=utf-8
import os,sys
print "current pid: ", os.getpid()
print sys.argv
len = len(sys.argv)
for i in range(len):
print sys.argv[i]
【说明】
os.execl函数运行了testExec.py文件,并传递了两个参数,分别为,1,2。执行之后的结果:
同样的,使用os.system也可以实现上面的效果:
import os
statCode = os.system("python testExec.py 1 2 3")
【说明】
os.system执行成功之后返回0,os.exec家族没有返回值,执行失败会触发OSError。
os.system是调用系统内置命令来执行操作,命令操作结束之后会将控制权返回到启动进程,os.exec家族函数在执行命令之后会接管启动进程,而不是返回控制权,这样启动新进程的进程在执行os.exec家族函数之后便终止。
2.2使用subprocess管理进程
import subprocess
import os
print os.getpid()
pingP = subprocess.Popen("ping www.baidu.com", shell=True)
print pingP.pid
print pingP.returncode
运行效果:
3. 进程终止
os和sys模块分别提供了os.abort和sys.exit方法来终止进程,sys.exit()方法较为温和,在退出之前会执行一些清理操作,同时将执行的返回值返回,如果返回0表示进程退出成功,os.abort函数回直接向进程发送终止信号,一般情况下进程会立即终止,并不会进行相关的清理操作。
二.线程
Python中对线程的支持有两种方式:
| 低级线程模块 thread
| 高级线程模块 threading
threading模块是基于thread的,Python中提供了Queue模块实现多线程数据同步,Queue使用了FIFO(first in first out)队列模型。
1.线程创建
使用thread模块创建多线程:
import thread
import time
def worker(index, create_time):
print index, ' thread'
for i in range(5):
time.sleep(1)
# start_new_thread函数的第二个参数是一个元组
thread.start_new_thread(worker, (i, time.time()))
【说明】
thread中还有其他的方法:
start_new_thread 生成一个新的线程,并返回其标示符
exit 退出当前线程,触发SystemError
get_ident 获取当前线程的标示符
stack_size 获取线程堆栈的大小
使用高级模块threading创建新的线程:
# coding=utf-8
import threading
import time
class ThreadE(threading.Thread):
def __init__(self, index, time_create):
threading.Thread.__init__(self)
self.time_create = time_create
self.index = index
def run(self):
time.sleep(1)
print 'Thread Index:', self.index
print 'Thread Name:', self.getName()
pass
for i in range(5):
thread = ThreadE(i, time.time())
thread.start()
自定义一个类,需要继承自threading.Thread,需要实现run()方法,在run()方法中进行线程操作。
2.线程的私有变量
Java多线程中,每一个线程有一个ThreadLocal容器用来存储线程的私有变量,在Python中同样提供了类似的容器:
import threading
import time
class ThreadE():
def __init__(self, index):
self.local = threading.local()
self.index = index
def run(self):
self.local.index = self.index
time.sleep(1)
print 'Thread Index:', self.local.index, self.index
pass
e = ThreadE(2)
t = threading.Thread(target=e.run)
t.start()
【说明】
上面的代码除了演示threadinf.local的用法之外,还可以看出threading创建多线程的另外一种方法,和Java创建多线程中的Thread和Runnable相似。
3.Python线程的锁机制
Python多线程的低级模块和高级模块中分别提供了线程锁的机制:
| 低级模块thread -> thread.allocate_lock()
| 高级模块threading -> threading.Lock()
在thread中:
# coding=utf-8
import thread, time
import threading
class Counter():
def __init__(self):
self.value = 0
self.lock = thread.allocate_lock()
pass
def increment(self):
self.lock.acquire()
self.value = self.value +1
self.lock.release()
v = self.value
return v
couter = Counter()
class ThreadExample(threading.Thread):
def __init__(self, index):
threading.Thread.__init__(self)
self.index = index
pass
def run(self):
time.sleep(1)
c = "{0} --- {1}".format(self.index, couter.increment())
print c
for i in range(10):
t = ThreadExample(i)
t.start()
在threading中:
class Counter():
def __init__(self):
self.value = 0
self.lock = threading.Lock()
pass
def increment(self):
self.lock.acquire()
self.value = self.value +1
self.lock.release()
v = self.value
return v
运行的效果:
0 --- 1
1 --- 2
3 --- 3
2 --- 4
4 --- 5
7 --- 6
5 --- 7
6 --- 8
8 --- 9
9 --- 10
【说明】
上面运行的效果可以看到,虽然线程的index并不是按照顺序运行的,但是Counter的增长却是按照顺序增长的,可见两种锁机制都可以实现数据同步的功能。
4.Python的同步队列
Python提供了Queue模块,该模块中提供了一个FIFO的队列,可以实现数据的同步:
#coding=utf-8
import threading
import Queue, time
class WorkThread(threading.Thread):
def __init__(self, index, queue):
threading.Thread.__init__(self)
self.index = index
self.queue = queue
pass
def run(self):
while True:
time.sleep(1)
item = self.queue.get()
if item is None:
break
# 此处若为continue,线程将一直运行在后台
# continue
c = "{0} --- {1}".format(self.index, item)
print c
queue = Queue.Queue(0)
for index in range(5):
WorkThread(index, queue).start()
for i in range(10):
queue.put(i)
运行的结果:
0 --- 0
1 --- 1
2 --- 2
3 --- 4
4 --- 3
0 --- 5
1 --- 6
2 --- 7
4 --- 8
3 --- 9
【说明】
Queue模块中的Queue构造函数可以实例化一个FIFO的同步队列,Queue的构造函数中有一个maxsize的参数,当maxsize=0表示队列长度无限制,Queue实例使用put(task)将任务task存储到同步队列中,使用get()方法获取并移除同步队列中的任务。