创建线程对象的方式:
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()