多线程
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
threading
启动一个线程就是把一个函数传入并创建threading.Thread( )实例,然后调用start( )开始执行:
实例:
import threading #导入线程模块
import time#线程函数
def foo(num):print('我的进程名字为:'+threading.current_thread().name)time.sleep(2)print(num)#创建进程 #callback:回调函数 一般用来接收子进程运行函数的返回值
t1 = threading.Thread(target=foo,args=(1,),callback=func)
t2 = threading.Thread(target=foo,args=(2,))#启动线程
t1.start()
t2.start()#获取线程运行状态 True为运行 False为未运行
print(t1.is_alive())
print(t2.is_alive())#等待所有线程运行完毕在继续往下执行
t1.join()
t2.join()
执行结果为:
我的进程名字为:Thread-1
我的进程名字为:Thread-2
True #运行状态
True #运行状态
1 #第一个线程打印
2 #第二个线程打印
- threading.current_thread( ) 它返回当前线程的实例
- join( )在子线程执行完成之前,这个子线程的父线程将一直被阻塞。就是说,当调用join()的子进程没有结束之前,主进程不会往下执行。对其它子进程没有影响。
- is_alive() 获取线程的运行状态
守护进程setDaemon(True)
将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就兵分两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法。
实例:
import threading
from time import ctime,sleep
import timedef music(func):for i in range(2):print ("Begin listening to %s. %s" %(func,ctime()))sleep(4)print("end listening %s"%ctime())def move(func):for i in range(2):print ("Begin watching at the %s! %s" %(func,ctime()))sleep(5)print('end watching %s'%ctime())threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2)if __name__ == '__main__':for t in threads:t.setDaemon(True)t.start()print ("all over %s" %ctime())
执行结果为:
Begin listening to 七里香. Thu Sep 29 15:45:32 2016 #t1和t2启动,分别打印一次后sleep,主进程继续
Begin watching at the 阿甘正传! Thu Sep 29 15:45:32 2016
all over Thu Sep 29 15:45:32 2016 #主进程结束,程序结束
Lock互斥锁
多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
实例:
import threadingnum = 0
def add():global numfor i in range(1000000):num += 1t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)t1.start()
t2.start()t1.join()
t2.join()print(num)
运行结果为:
1183554
为什么会出现这样的问题呢 上面已经说过了,线程间任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。这种情况要使用lock (互斥锁)
实例:
import threadingnum = 0
def add():global numfor i in range(1000000):lock.acquire()#上锁num += 1lock.release()#解锁#也可以使用with方法效果相同# with lock:# num += 1t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
#创建互斥锁 只能有一个进程同时访问
lock = threading.Lock()
t1.start()
t2.start()t1.join()
t2.join()print(num)
运行结果为:
2000000
信号量
信号量是允许同一时间同时几个线程访问共享变量
import threading,timedef foo(i):# 上锁sem.acquire()time.sleep(1)print(i)# 解锁sem.release()# 锁 --- 信号量
sem = threading.BoundedSemaphore(3)for i in range(10):t = threading.Thread(target=foo,args=(i,))t.start()
自己体验执行结果会更容易理解信号量
Event(事件)
Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果Flag值为 False,那么当程序执行 event.wait方法时就会阻塞,如果Flag值为True,那么event.wait 方法时便不再阻塞。Event没有锁,无法使线程进入同步阻塞状态。
set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
import threading
import timeevent = threading.Event()def chihuoguo(name):# 等待事件,进入等待阻塞状态print '%s 已经启动' % threading.currentThread().getName()print '小伙伴 %s 已经进入就餐状态!'%nametime.sleep(1)event.wait()# 收到事件后进入运行状态print '%s 收到通知了.' % threading.currentThread().getName()print '小伙伴 %s 开始吃咯!'%name# 设置线程组
threads = []# 创建新线程
thread1 = threading.Thread(target=chihuoguo, args=("a", ))
thread2 = threading.Thread(target=chihuoguo, args=("b", ))# 添加到线程组
threads.append(thread1)
threads.append(thread2)# 开启线程
for thread in threads:thread.start()time.sleep(0.1)
# 发送事件通知
print '主线程通知小伙伴开吃咯!'
event.set()
运行结果:
Thread-1 已经启动
小伙伴 a 已经进入就餐状态!
Thread-2 已经启动
小伙伴 b 已经进入就餐状态!
主线程通知小伙伴开吃咯!
Thread-1 收到通知了.
小伙伴 a 开始吃咯!
Thread-2 收到通知了.
小伙伴 b 开始吃咯!
使用queue和threading创建一个线程池
由于threading没有自带线程池 所以我们需要手动写
实例:
from queue import Queue
import threading
from multiprocessing import Pool
import timeclass ThreadPool(object):def __init__(self,max_num):# 设置线程队列self.thread_q = Queue(max_num)# 往队列里添加线程类对象for i in range(max_num):self.thread_q.put(threading.Thread)# 获取一个线程def get_thread(self):return self.thread_q.get()# 添加一个线程def add_thread(self):self.thread_q.put(threading.Thread)# 使用线程池
def foo(i,pool):print(i)time.sleep(1)pool.add_thread()if __name__ == '__main__':# 创建线程池pool = ThreadPool(2)# 循环创建任务for i in range(1,11):# 从线程池拿取一个线程Thread = pool.get_thread()# 创建线程任务t = Thread(target=foo,args=(i,pool))# 启动任务t.start()
Threading.Local
threadLocal解决了参数在一个线程中各个函数之间相互传递的问题
一个threadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本 互不干扰
实例:
import threading
#创建全局ThreadindLocal对象
local = threading.local()
class Stu():def __inif__(self,name):self.name = name
def process_stu(name):std = (name)#向对象中添加属性 这个属性值谁调用就是谁的local.stu = stddo_task_1()do_task_2()
def do_task_1():std = local.stuprint('do_task_1',std)def do_task_2():std = local.stuprint('do_task_2',std)if __name__ == "__main__":t1 = threading.Thread(target=process_stu,args=('小明',))t2 = threading.Thread(target=process_stu,args=('小亮',))t1.start()t2.start()t1.join()t2.join()
全局变量local,每个线程对它都可以读写`stu`属性 而且互不影响可以吧local看成全局变量,但每个属性如local.stu都是线程的局部变量
可以任意读写互不干扰,也不用管理锁的问题(threading内部处理)
可以理解为全局变量local是一个字典 不但可以用local.stu 还可以绑定其他变量
threadLocal最常用的地方就是为每一个线程绑定一个数据库线程,Http请求,用户身份信息等
这样一个线程的所有调用到的处理函数都可以非常方便的访问这些资源