参考
多线程
线程是操作系统直接支持的执行单元
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
# -*- coding:utf-8 -*-
import threading
import time
def loop():
print 'thread %s is runing...' % threading.current_thread().name
n = 0
while n<5:
n = n+1
print 'thread %s >>> %s' %(threading.current_thread().name,n)
time.sleep(1)
print 'thread %s is runing...' % threading.current_thread().name
t = threading.Thread(target = loop,name = 'LoopThread')
t.start()
t.join()
print 'thread %s ended ' % threading.current_thread().name
thread MainThread is running...#主线程执行
thread LoopThread is running...#子线程执行
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended. #主线程执行
主线程
进程默认就会启动一个线程,该线程称为主线程
主线程又可以启动新的线程
current_thread()
Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。
主线程实例的名字叫MainThread,子线程的名字在创建时指定,用LoopThread命名子线程
t = threading.Thread(target = loop,name = 'LoopThread')
Lock
多线程和多进程最大的不同
多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,
而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,
因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
# -*- coding:utf-8 -*-
import threading
import time
balance = 0
def change_it(n):
global balance
#先加后减应为零
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
change_it(n)
t1 = threading.Thread(target = run_thread,args=(5,))
t2 = threading.Thread(target = run_thread,args=(8,))
t1.start()
t2.start()
t1.join()#主线程调用了ti.join()-->在该处等待t1子线程完成操作才能继续执行
t2.join()
print balance
高级语言的一条语句在CPU执行时是若干条语句,即使一个简单的计算:
balance = balance + n
也分两步:
1. 计算balance + n,存入临时变量中;
2. 将临时变量的值赋给balance。
看成:
x = balance + n
balance = x
代码正常执行时:
初始值 balance = 0
t1: x1 = balance + 5 # x1 = 0 + 5 = 5
t1: balance = x1 # balance = 5
t1: x1 = balance - 5 # x1 = 5 - 5 = 0
t1: balance = x1 # balance = 0
t2: x2 = balance + 8 # x2 = 0 + 8 = 8
t2: balance = x2 # balance = 8
t2: x2 = balance - 8 # x2 = 8 - 8 = 0
t2: balance = x2 # balance = 0
结果 balance = 0
t1和t2是交替运行的,如果操作系统以下面的顺序执行t1、t2:
初始值 balance = 0
t1: x1 = balance + 5 # x1 = 0 + 5 = 5
t2: x2 = balance + 8 # x2 = 0 + 8 = 8
t2: balance = x2 # balance = 8
t1: balance = x1 # balance = 5
t1: x1 = balance - 5 # x1 = 5 - 5 = 0
t1: balance = x1 # balance = 0
t2: x2 = balance - 5 # x2 = 0 - 5 = -5
t2: balance = x2 # balance = -5
结果 balance = -5
究其原因
是因为修改balance需要多条语句,而执行这几条语句时,线程可能中断,从而导致多个线程把同一个对象的内容改乱了。
确保balance计算正确
就要给change_it()上一把锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。
同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
#先获取锁
lock.acquire()
try:
change_it(n)
finally:
#一定释放
lock.release()
多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程
try...finally
来确保锁一定会被释放
阻止了多线程并发执行
join()
join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
join([timeout])
参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的。
主进程挨个调用子线程的join()方法。当四个线程都执行完毕后,主线程才会执行下面的代码,在这里也就是退出了。
setDaemon()
setDaemon,在底层的thread模块中,只要 主线程结束
了,所有的 其它线程都会结束
,这很明显,主线程结束python将销毁运行时环境,主线程肯定会被结束。
threading模块的线程setDaemon就是为了解决这个问题的setDaemon(True),那么和之前一样,主线程结束,所有子线程都将结束。
setDaemon(False),主线程将等待该线程结束,等同于你调用线程的join方法。