python多线程之threading
一。创建线程的方法
1.用 thread.Thread 直接在线程中运行函数
import threading def threadfun(x,y): #线程任务函数 threadfun() for i in range(x,y): print(i) ta = threading.Thread(target=threadfun,args=(1,6)) #创建一个线程ta,执行 threadfun() tb = threading.Thread(target=threadfun,args=(10,15)) #创建一个线程tb,执行threadfun() ta.start() #调用start(),运行线程 tb.start() #调用start(),运行线程 '''''打印:1 2 3 4 5 10 11 12 13 14'''
2.通过继承 thread.Thread 类 来创建线程
这种方法只需要重载 threading.Thread 类的 run 方法,然后调用 start()开启线程就可以了
import threading class mythread(threading.Thread): def run(self): for i in range(1,5): print(i) ma = mythread(); mb = mythread(); ma.start() mb.start()
3.创建Thread实例,传给它一个可调用的类的实例
import threading from time import sleep,ctime loops=[4,2] class ThreadFunc(object): def __init__(self,func,args,name=''): self.name=name self.func=func self.args=args def __call__(self): self.func(*self.args) def loop(nloop,nsec): print 'start loop',nloop,'at:',ctime() sleep(nsec) print 'loop',nloop,'done at:',ctime() def main(): print 'starting at:',ctime() threads=[] nloops=range(len(loops)) nloop=range(len(loops)) for i in nloops: t=threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() print 'alll DONE at:',ctime() if __name__=='__main__': main()
3.join方法
join()作用是 调用 join() 的线程 阻塞直到 某一线程结束才继续执行
import threading import time class mythread(threading.Thread): def run(self): self.i = 1 print('%d'%(self.i)) self.i = self.i+1 time.sleep(1) #睡眠一秒 print('%d'%(self.i)) time.sleep(1) if __name__ == '__main__': ta = mythread() #实例化线程 ta.start() #开启ta线程 ta.join() #主线程等待 ta线程结束才继续执行 print('main thread over')
4.isAlive()方法
这个方法用于判断线程是否存在
1.当线程未调用 start()来开启时,isAlive()会返回False
2.但线程已经执行后并结束时,isAlive()也会返回False
import threading import time class mythread(threading.Thread): def run(self): time.sleep(2) if __name__ == '__main__': ta = mythread() #实例化线程 print(ta.isAlive()) #打印False,因为未执行 start()来使ta线程运行 ta.start() print(ta.isAlive()) #打印Ture,因为ta线程运行了 time.sleep(3) print(ta.isAlive()) #打印False,因为ta线程已经结束了
5.Thread对象数据属性,name(线程名),ident(线程的标识符),daemon(布尔标识,表示这个线程是否是守护线程)
5.1设置为守护线程的方法:在启动线程之前执行:thread.daemon=True.查询是否为守护线程,isDaemon(),设置线程的守护标志为布尔值,setDaemon(daemonic)在start()之前调用
5.2name属性表示线程的线程名 默认是 Thread-x x是序号,由1开始,第一个创建的线程名字就是 Thread-1
import threading import time class mythread(threading.Thread): def run(self): pass if __name__ == '__main__': ta = mythread() #实例化线程 ta.name = 'thread-ta' tb = mythread() tb.start() ta.start() print(ta.name) #打印 thread-ta print(tb.name) #打印 Thread-2
5.3 daemon属性用来设置线程是否随主线程退出而退出
当 daemon = False 时,线程不会随主线程退出而退出(默认时,就是 daemon = False)
当 daemon = True 时,当主线程结束,其他子线程就会被强制结束
import threading import time class mythread(threading.Thread): def run(self): time.sleep(2) print('my thread over') def main(): ta = mythread() ta.daemon = True ta.start() print('main thread over') if __name__ == '__main__': main() #打印结果 :main thread over 然后马上结束程序
6.线程同步———锁
当一个进程拥有多个线程之后,如果他们各做各的任务互没有关系还行,但既然属于同一个进程,他们之间总是具有一定关系的。比如多个线程都要对某个数据进行修改,则可能会出现不可预料的结果。为保证操作正确,就需要引入锁来进行线程间的同步。
python3 中的 threading 模块提供了 RLock锁(可重入锁)。对于某一时间只能让一个线程操作的语句放到 RLock的acquire 方法 和 release方法之间。即 acquire()方法相当于给RLock 锁 上锁,而 release() 相当于解锁。
import threading import time class mythread(threading.Thread): def run(self): global x #声明一个全局变量 lock.acquire() #上锁,acquire()和release()之间的语句一次只能有一个线程进入,其余线程在acquire()处等待 x += 10 print('%s:%d'%(self.name,x)) lock.release() #解锁 x = 0 lock = threading.RLock() #创建 可重入锁 def main(): l = [] for i in range(5): l.append(mythread()) #创建 5 个线程,并把他们放到一个列表中 for i in l: i.start() #开启列表中的所有线程 if __name__ =='__main__': main()
打印结果:
Thread-1:10
Thread-2:20
Thread-3:30
Thread-4:40
Thread-5:50
7.线程的同步——Event对象
Event对象存在于 threading 模块中。Event 实例管理着 一个内部标志,通过 set() 方法来将该标志设置成 True,使用 clear() 方法将该标志重置成 False
wait() 方法会使当前线程阻塞直到标志被设置成 True,wait()可以选择给他一个参数,代表时间,代表阻塞多长时间,若不设置就是阻塞直到标志被设置为True
isSet()方法 :能判断标志位是否被设置为True
import threading import time class Mon(threading.Thread): def run(self): Dinner.clear() print('Cooking dinner') time.sleep(3) Dinner.set() #标志设置为True print(self.name,':dinner is OK!') class Son(threading.Thread): def run(self): while True: if Dinner.isSet(): #判断标志位是否被设置为True break else: print('dinner isnot ready!') Dinner.wait(1) print(self.name,':Eating Dinner') def main(): mon = Mon() son = Son() mon.name = 'Mon' son.name = 'Son' mon.start() son.start() if __name__ == '__main__': Dinner = threading.Event() main() ''''' Cooking dinner dinner isnot ready! dinner isnot ready! dinner isnot ready! Mon :dinner is OK! Son :Eating Dinner '''
8.线程的同步----Condition条件变量
条件变量表示当线程满足某一个 条件才被唤醒,否则一直阻塞
对比 只用锁不用条件变量 的好处就是:只用锁的话,如果一个线程在上锁后,解锁前,因为某一条件一直阻塞着,那么锁就一直解不开,那么其他线程也就因为一直获取不了锁而跟着阻塞着,这样效率就不好,浪费了很多时间。对于这种情况,锁+条件变量可以让该线程先 解锁,然后阻塞着,等待条件满足了,再重新唤醒并获取锁(上锁)。这样就不会因为一个线程阻塞着而影响其他线程也跟着阻塞了。
Condition 提供的方法:
acquire() 和 release() 表示上锁和解锁,和 单纯的锁机制一样。
wait() 解开锁,阻塞,直到其他线程调用了notify()或者notifyAll才被唤醒,注意,这里的wait()跟上面Event提到的wait()不是同一样东西
notify() 发出资源可用的信号,唤醒任意一条因 wait()阻塞的进程
notifyAll() 发出资源可用信号,唤醒所有因wait()阻塞的进程
下面给出一个例子,一家蛋糕店:只会做一个蛋糕,卖出后才会再做一个。绝对不会做积累到2个蛋糕。
import threading import time class Server(threading.Thread): def run(self): global x while True: con.acquire() while x>0: con.wait() x += 1 time.sleep(1) print(self.name,':I make %d cake!'%(x)) con.notifyAll() con.release() class Client(threading.Thread): def run(self): global x con.acquire() while x == 0: con.wait() x-=1 print(self.name,'I bought a cake! the rest is %d cake'%(x)) con.notifyAll() con.release() def main(): ser = Server() ser.name = 'Cake Server' client = [] for i in range(3): client.append(Client()) ser.start() for c in client: c.start() if __name__ =='__main__': x = 0 con = threading.Condition() main() ''''' 打印结果: Cake Server :I make 1 cake! Thread-3 I bought a cake! the rest is 0 cake Cake Server :I make 1 cake! Thread-4 I bought a cake! the rest is 0 cake Cake Server :I make 1 cake! Thread-2 I bought a cake! the rest is 0 cake Cake Server :I make 1 cake! '''