创建线程对象的方式:
Thread类直接创建
import threading import time def countNum(n): # 定义某个线程要运行的函数 print("running on number:%s" %n) time.sleep(3) if __name__ == ‘__main__‘: t1 = threading.Thread(target=countNum,args=(23,)) #生成一个线程实例 t2 = threading.Thread(target=countNum,args=(34,)) t1.start() #启动线程 t2.start() print("ending!")
Thread类继承创建
#继承Thread式创建 import threading import time class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) # 用父类的init self.num=num def run(self): print("running on number:%s" %self.num) time.sleep(3) t1=MyThread(56) # 线程对象 t2=MyThread(78) t1.start() # 激活线程对象 t2.start() print("ending")
线程安全
当多个线程处理公共数据,会出现数据紊乱现象
import threading import time def sub(): global num temp=num time.sleep(0.1 )
num=temp-1 time.sleep(2) num=100 l=[] lock=threading.Lock() for i in range(100): t=threading.Thread(target=sub,args=()) t.start() l.append(t) for t in l: t.join() print(num)
多个线程抢GIL锁,当在sleep状态下的线程,不会去抢GIL锁,其他的线程在抢的时候,之前的不会醒过来,每个线程中都有一个temp=100,当第一个醒来时,进行-1操作,得到99赋值给全局的num,然后再进行sleep,当第二个线程清醒过来,也是用temp=100 减去1,得到99,依次类推,对num赋值99,100次赋值后,结果还是99
如果所有线程执行完之前,有线程清醒,那么就会去抢GIL锁,线程切换之前就把temp赋值操作完成了,所以不进行sleep(0.1)的时候,结果为0
互斥锁
全局的一把锁,将代码串行化,保证数据安全
锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:
import threading R=threading.Lock() R.acquire() ‘‘‘ 对公共数据的操作 ‘‘‘ R.release()
import threading import time def sub(): global num lock.acquire() #获取锁 temp=num time.sleep(0.001 ) num=temp-1 lock.release() # 释放锁 # 只能被一个线程占用,后面的线程会等待拿到锁,只有被释放后第二个线程才能继续执行 time.sleep(2) num=100 l=[] lock = threading.Lock() # 互斥锁对象 for i in range(100): t=threading.Thread(target=sub,args=()) t.start() l.append(t) for t in l: t.join() print(num)
死锁:
两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
import threading import time class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) # 继承父类 def run(self): self.foo() self.bar() def foo(self): LOCKA.acquire() print("%s LOCKA %s"%(self.name,time.ctime())) LOCKB.acquire() print("%s LOCKB %s"%(self.name,time.ctime())) LOCKB.release() LOCKA.release() def bar(self): LOCKB.acquire() print("%s LOCKB %s" % (self.name, time.ctime())) time.sleep(1) # 这里sleep1的时候,A锁已经被其他线程拿到了,这里就拿不到A锁了 LOCKA.acquire() print("%s LOCKA %s" % (self.name, time.ctime())) LOCKA.release() LOCKB.release() LOCKA=threading.Lock() LOCKB=threading.Lock() for i in range(10): t=MyThread() t.start() # Thread-1 LOCKA Sat Feb 24 15:41:34 2018 # Thread-1 LOCKB Sat Feb 24 15:41:34 2018 # Thread-1 LOCKB Sat Feb 24 15:41:34 2018 # Thread-2 LOCKA Sat Feb 24 15:41:34 2018
递归锁
在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
原理:
这把锁允许多次acquire和release,只要计数器大于0,其他线程就没有资格去抢这把锁
import threading import time class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) # 继承父类 def run(self): self.foo() self.bar() def foo(self): Rlock.acquire() # 计数1 print("%s LOCKA %s"%(self.name,time.ctime())) Rlock.acquire() # 计数2 print("%s LOCKB %s"%(self.name,time.ctime())) Rlock.release() Rlock.release() # 计数 0 def bar(self): Rlock.acquire() print("%s LOCKB %s" % (self.name, time.ctime())) time.sleep(1) # 这里sleep1的时候,A锁已经被其他线程拿到了,这里就拿不到A锁了 Rlock.acquire() print("%s LOCKA %s" % (self.name, time.ctime())) Rlock.release() Rlock.release() # LOCKA=threading.Lock() # LOCKB=threading.Lock() Rlock = threading.RLock() for i in range(10): t=MyThread() t.start()
信号量 Semaphore
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()
作用:允许同时开多少个进程
import threading import time semaphore = threading.Semaphore(5) # 同时又5个线程,最大连接数5个 def func(): semaphore.acquire() time.sleep(2) print("ok") semaphore.release() for i in range(100): t=threading.Thread(target=func,args=()) t.start()