python多线程理解

在发送网络请求的过程中,单个请求的速度总是有着很大的限制,而任务往往需要以更快的速度去执行,这时多线程就是一个很好地选择。python已经给我们封装好了多线程库thread和threading。

thread:比较底层的模块
threading:Higher-level threading interface

ps:建议使用threading模块
- 高级别的threading模块更为先进,对线程的支持更为完善
- 低级别的thread模块同步原语很少
- thread模块对线程什么时候结束完全没有控制,当主线程结束时,所有线程都会强制结束

thread
模块函数

start_new_thread(function, args,kwargs=None): 产生新的线程,args是function的参数,没有时写(),kwargs用来调用这个函数
allocate_lock(): 分配锁,LockType类型
exit(): 让线程退出

LockType的操作

acquire(wait=None):尝试获取锁
locked(): 获取了锁返回True,没有返回False
release():释放锁

Demo1

$ cat t1.py
import thread
from time import sleep

def a():
print "a start"
sleep(2)
print "a end"

def b():
print "b start"
sleep(2)
print "b end"

def main():
thread.start_new_thread(a,())
thread.start_new_thread(b,())
print "all done"

if __name__ == "__main__":
main()

$ python t1.py
all done
b start
a start
最终会发现,每一次运行出来的结果都有可能不同,但是绝对不会出现“a end”和“b end”。这是为什么呢,这里没有写让主线程停下来等所有子线程结束后再继续运行的代码,所以main线程在执行完print "all done"就关闭了a和b两个线程。怎么办呢,可以在这里加一个sleep等待子进程执行完毕后再退出。

Demo2: thread -- 多线程的演示 by sleep

$ cat t2.py
import thread
from time import sleep

def a():
print "a start"
sleep(2)
print "a end"

def b():
print "b start"
sleep(2)
print "b end"

def main():
thread.start_new_thread(a,())
thread.start_new_thread(b,())
sleep (4) ----防止主进程过早退出,加sleep等待子进程执行完毕后再推出
print "all done"

if __name__ == "__main__":
main()
$ python t1.py
b start
a start
a end
b end
all done
但是假设我们不知道子进程执行的时间怎么办,这就是锁的用武之地了。因为使用锁要比使用sleep()函数更为合理。如下所示:

Demo3: thread -- 多线程演示 by lock

实现方式为: 主线程初始化两个锁,分别传给两个函数,两个函数在执行完自己的代码后释放锁,主线程一直在轮询这个锁有没有释放,如果释放了就退出。

def a(lock, nsec):
print "a starting at :", ctime()
sleep(nsec)
lock.release() -- 执行完之后释放锁
print "a end", ctime()

def b(lock, nsec):
print "b starting at :", ctime()
sleep(nsec)
lock.release() -- 执行完之后释放锁
print "b end", ctime()

def main():
print "Demo Starting at:", ctime()

locks = []

# Initialize lock -- 主线程先获取两个锁,占为己有
for i in range(2):
lock = thread.allocate_lock()
lock.acquire()
locks.append(lock)

# 每个进程分配一个锁
thread.start_new_thread(a, (locks[0],2))
thread.start_new_thread(b, (locks[1],4))

for i in range(2): #一直在轮询,看锁有没有释放
while locks[i].locked(): pass

print "all done at:", ctime()
最后的结果为:

$ python thread_demo.py
Demo Starting at: Fri Aug 29 22:03:01 2014
a starting at : Fri Aug 29 22:03:01 2014
b starting at : Fri Aug 29 22:03:01 2014
a end Fri Aug 29 22:03:03 2014
b end Fri Aug 29 22:03:05 2014
all done at: Fri Aug 29 22:03:05 2014
不难发现,thread库的同步机制比较难用,一切都需要主进程来处理。并且没有守护进程,主进程一退,整个世界都会变得很清静。而threading库给我们提供了守护进程。下面就来看看threading的简单用法。

threading
threading提供了Thread类,还提供了很多非常好用的同步机制。感觉重点了解Thread类就可以,多线程,也就是通过Thread类的多个实例。 类的主要方法有:

start():开始线程的执行。thread库里里面,是没有办法控制线程的开始的
join(timeout=None): 等待线程结束,有点类似Demo3中的轮询
run():定义线程的功能

感觉上面是比较重要的,立马就会用到的。还有一些其他的:

getName():获取线程名
setName(name):设置线程名
isAlive(): 返回bool 表示线程是否在运行中
activeCount():返回运行中的线程数
currentThread():返回当前线程对象
enumerate():返回当前活动线程的列表
isDaemon(): 返回线程的Daemon标志
setDaemon(daemonic): 设置线程的Daemon标志,一般在start()函数前调用
settrace(func):为所有线程设置跟踪函数
setprofile(func): 为所有线程设置profile函数

Demo4 -- threading演示

def loop(i, nsec):
print "thread %d starting at : %s" %(i, ctime())
sleep(nsec)
print "thread %d end at : %s" %(i, ctime())

def main():
threads = []
loops = [2, 4]
# 实例化进程
for i in range(len(loops)):
t = threading.Thread(target = loop, args = (i, loops[i]))
threads.append(t)

for i in range(len(loops)):
threads[i].start()

for i in range(len(loops)):
threads[i].join()

print "all done"
最后的结果为:

thread 0 starting at : Sun Aug 31 13:31:28 2014
thread 1 starting at : Sun Aug 31 13:31:28 2014
thread 0 end at : Sun Aug 31 13:31:30 2014
thread 1 end at : Sun Aug 31 13:31:32 2014
all done

对Daemon线程理解:
A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left. The initial value is inherited from the creating thread. The flag can be set through the daemon property.
线程可以被标识为"Daemon线程",Daemon线程表明整个Python主程序只有在Daemon子线程运行时可以退出。该属性值继承自父线程,可通过setDaemon()函数设定该值。

Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.
注意:Daemon线程会被粗鲁的直接结束,它所使用的资源(已打开文件、数据库事务等)无法被合理的释放。因此如果需要线程被优雅的结束,请设置为非Daemon线程,并使用合理的信号方法,如事件Event。

Python主程序当且仅当不存在非Daemon线程存活时退出。
即:主程序等待所有非Daemon线程结束后才退出,且退出时会自动结束(很粗鲁的结束)所有Daemon线程。
亦理解为:Daemon设置为子线程是否随主线程一起结束,默认为False。如果要随主线程一起结束需要设置为True。

Daemon线程用途:
Daemons are only useful when the main program is running, and it's okay to kill them off once the other non-daemon threads have exited. Without daemon threads, we have to keep track of them, and tell them to exit, before our program can completely quit. By setting them as daemon threads, we can let them run and forget about them, and when our program quits, any daemon threads are killed automatically.
Daemon线程当且仅当主线程运行时有效,当其他非Daemon线程结束时可自动杀死所有Daemon线程。如果没有Daemon线程的定义,则必须手动的跟踪这些线程,在程序结束前手动结束这些线程。通过设置线程为Daemon线程,则可以放任它们运行,并遗忘它们,当主程序结束时这些Daemon线程将自动被杀死。

posted on 2017-08-27 21:33  niansi  阅读(177)  评论(0编辑  收藏  举报

导航