线程的创建:
1.线程的创建【使用此方式创建的线程为守护线程】
import _thread
import threadingdef fn1(a, b):
print(a, b)
print(threading.current_thread().name)def create_thread1():
# 参数一:在子线程中要执行的函数
# 参数二:子线程中的函数需要的参数,注意一定要以元组的形式传参
# 创建并执行守护线程,守护线程的特点是,当主线程结束,守护线程无论执行完毕都会结束
_thread.start_new_thread(fn1, ("hello", "yes"))
if __name__ == "__main__":
create_thread1()
print(threading.current_thread().name)
>>>MainThread
#注意此时创建的线程中的代码不会执行,原因是
#主线程执行的速度非常快,主线程执行结束,就直接退出了,因此我们的子线程根本不会被创建
#我们可以让主线程阻塞一段时间可以查看一下效果
#time.sleep(3) #这时候我们就可以查看打印的数据了
2、线程创建的方式二(比较常用)
import threading
def func1(*args):
print(args)
print(threading.current_thread().name)def create_thread2():
# target:在子线程中要执行的函数
# deamo:是否为守护线程
# name:线程名称
t = threading.Thread(target=func1, daemon=False, name="BIG", args=("hello"))
t.start() # 启动线程if __name__ == "__main__":
create_thread2()
3、创建线程方式三
import threading
class MyThread(threading.Thread):
def __init__(self):
super().__init__()
#一定要重写此方法,此方法会自动被调用
#里面的子线程
def run(self):
print("mythread",threading.current_thread().name)def create_thread3():
t = MyThread()
t.start()
if __name__ == "__main__":
create_thread3()
参数及常用函数:
Thread(group=None, target=None, name=None, args=(), kwargs={})
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 线程名;
args/kwargs: 要传入方法的参数。
实例方法:
isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。
get/setName(name): 获取/设置线程名。
start(): 线程准备就绪,等待CPU调度
is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置)
#如果是后台线程/守护线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止
#如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)
多线程:
import threading,time,random
def fun(*args):
time.sleep(random.randint(1,4))
print("子线程",args)
if __name__ == "__main__":
#多个线程同时运行,是异步执行
t1 = threading.Thread(target=fn,args=("包饺子",))
t1.start()
# t1.join() 会阻塞 等待t1线程执行完成后再继续往后执行
t2 = threading.Thread(target=fn,args=("甜馨",))
t2.start()
t3 = threading.Thread(target=fn,args=("嗯哼",))
t3.start()
#若多个线程想让其按顺序执行,则可以使用join() 的方法
循环创建线程:
import threading,time,random
def fun(*args):
time.sleep(random.randint(1,4))
print("子线程",args)
if __name__ == "__main__":
start = time.time()
t_list= []
for x in range(10):
t1 = threading.Thread(target=fn,args=(x,))
t1.start()
t_list.append(t1)
#当前线程的名称
print(threading.current_thread().name)
#线程id
print(t.ident)
#线程是否正在执行
print(t.is_alive())
#正在运行线程的数量,包括主线程
print(threading.active_count())
#列举当前正在运行的所有的线程
print(threading.enumerate())
for t in t_list:
t.join()
print(time.time()-start)
线程冲突:
多个线程并发访问同一个变量而相互干扰
解决方式:线程锁
import threading
def fn():
global num
#cpu分配的时间片不足以完成一百万次加法运算
#因此结果还没有被保存到内存中就可能被其他的线程打断
for x in range(1000000):
num += 1def thread1():
for i in range(5):
t = threading.Thread(target=fn)
t.start()
解决多线程的冲突问题方式一:
lock = threading.Lock()
使用 with lock
with内部实现了enter()和exit()执行语句之前调用enter方法,退出的时候调用exit
import threading
#解决线程冲突问题
#线程锁
lock = threading.Lock()
def fn():
with lock: #会自动加锁,执行完毕之后才会释放锁
global num
#cpu分配的时间片不足以完成一百万次加法运算
#因此结果还没有被保存到内存中就可能被其他的线程打断
for x in range(1000000):
num += 1
print(num)def thread1():
for i in range(5):
t = threading.Thread(target=fn)
t.start()if __name__ == '__main__':
num = 0
thread1()
解决多线程冲突问题,方式二
lock = threading.Lock()
lock.acquire() #锁定
lock.release() #释放锁
import threading#解决线程冲突问题
#线程锁,创建锁
lock = threading.Lock()def fn():
lock.acquire() #锁定
global num
#cpu分配的时间片不足以完成一百万次加法运算
#因此结果还没有被保存到内存中就可能被其他的线程打断
for x in range(1000000):
num += 1
print(num)
lock.release() #释放锁def thread1():
for i in range(5):
t = threading.Thread(target=fn)
t.start()if __name__ == '__main__':
num = 0
thread1()
死锁:
定义:是指一个资源多次调用,而多次调用对方都未能释放该资源就会造成一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或者系统产生了死锁。
若存在两个线程:线程A 与线程B
若线程A与线程B都 需要资源1与资源2才能执行
现在线程A拿到了资源1,线程B拿到了资源2,此时就构成了死锁。
若要解决死锁的问题,则此时我们需要使用递归锁
下面以一个关于线程死锁的经典问题:“哲学家就餐问题”,作为本节最后一个例子。题目是这样的:五位哲学家围坐在一张桌子前,每个人 面前有一碗饭和一只筷子。在这里每个哲学家可以看做是一个独立的线程,而每只筷子可以看做是一个锁。每个哲学家可以处在静坐、 思考、吃饭三种状态中的一个。需要注意的是,每个哲学家吃饭是需要两只筷子的,这样问题就来了:如果每个哲学家都拿起自己左边的筷子, 那么他们五个都只能拿着一只筷子坐在那儿,直到饿死。此时他们就进入了死锁状态。 下面是一个简单的使用死锁避免机制解决“哲学家就餐问题”的实现:
import threading,time
class ZheXueJia():
def __init__(self,left,right):
self.left = left
self.right = rightdef run(z,name):
#获取左筷子
f = z.left.acquire()
if f:
print(name,"获取左筷子...")
#获取右筷子
ff = z.right.acquire()
if ff:
print(name,"获取右筷子...")
print("哲学家开始就餐",name)
time.sleep(1)
print("就餐完毕",name)
#释放右筷子
z.right.release()
#释放左筷子
z.left.release() if __name__ == '__main__':
#每个筷子相当于一把锁
rlock1 = threading.RLock()
rlock2 = threading.RLock()
rlock3 = threading.RLock()
rlock4 = threading.RLock()
rlock5 = threading.RLock()
#必须同时拥有两把锁才能就餐
z1 = ZheXueJia(rlock5, rlock1)
z2 = ZheXueJia(rlock1, rlock2)
z3 = ZheXueJia(rlock2, rlock3)
z4 = ZheXueJia(rlock3, rlock4)
z5 = ZheXueJia(rlock4, rlock5)
#创建五个线程模仿哲学家
t1 = threading.Thread(target=run,args=(z1,"z1"))
t2 = threading.Thread(target=run,args=(z2,"z2"))
t3 = threading.Thread(target=run,args=(z3,"z3"))
t4 = threading.Thread(target=run,args=(z4,"z4"))
t5 = threading.Thread(target=run,args=(z5,"z5"))
#开启线程
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
银行家算法:
import threading
import timeA = threading.Lock()
B = threading.Lock()class MyThread(threading.Thread):
def fun1(self):
A.acquire()
print(,"gotA",time.ctime())
time.sleep(2)
B.acquire()
print(,"gotB",time.ctime())
time.sleep(1)
B.release()
A.release() def func2(self):
B.acquire()
print(,"gotB",time.ctime())
time.sleep(2)
A.acquire()
print(,"gotA",time.ctime())
time.sleep(1)
A.release()
B.release() def run(self):
self.fun1()
self.func2()if __name__ == '__main__':
L = []
for i in range(5):
t = MyThread()
t.start()
L.append(t)
for i in L:
i.join()
print("over")
运行结果:
Thread-1 gotA Wed May 15 20:47:24 2019
Thread-1 gotB Wed May 15 20:47:26 2019
Thread-1 gotB Wed May 15 20:47:27 2019
Thread-2 gotA Wed May 15 20:47:27 2019
#结果会卡在这儿,原因就是第一个线程执行完fun1后开始执行fun2,而第二个开始执行fun1,它们会对锁产生抢占,所以导致运行卡住形成死锁现象。
或者使用递归锁来解决
# 解决方案:将所有锁改编成递归锁RLock
# 在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
# 这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。
# 直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁。
import threading
import time# mutexA = threading.Lock() #资源A
# mutexB = threading.Lock()#资源B
RLock = threading.RLock() class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) # 当对象调用start的时候会自动执行此方法
def run(self):
self.fun1()
self.fun2() def fun1(self):
RLock.acquire() # 如果锁被占用,则阻塞在这里,等待锁的释放
# 获取资源A
print("I am %s , get res: %s" % (, "ResA")) RLock.acquire()
print("I am %s , get res: %s" % (, "ResB"))
RLock.release() RLock.release()
def fun2(self):
RLock.acquire()
print("I am %s , get res: %s" % (, "ResB"))
time.sleep(0.2) RLock.acquire()
print("I am %s , get res: %s" % (, "ResA"))
RLock.release()
RLock.release()if __name__ == "__main__":
for i in range(0, 10):
my_thread = MyThread()
my_thread.start()
信号量:
semaphore是一个内置的计数器,限制同一时间执行的线程的个数
#每当调用acquire()时,内置计数器-1
#每当调用release()时,内置计数器+1
计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()
import time
import threadingdef foo():
time.sleep(2) #程序休息2秒
print("ok",time.ctime())for i in range(200):
t1=threading.Thread(target=foo,args=()) #实例化一个线程
t1.start() #启动线程
程序会在很短的时间内生成200个线程来打印一句话。
如果在主机执行IO密集型任务的时候再执行这种类型的程序时,计算机就有很大可能会宕机。这时候就可以为这段程序添加一个计数器功能,来限制一个时间点内的线程数量。
宕机,指操作系统无法从一个严重系统错误中恢复过来,或系统硬件层面出问题,以致系统长时间无响应,而不得不重新启动计算机的现象。它属于电脑运作的一种正常现象,任何电脑都会出现这种情况。
import time
import threadings1=threading.Semaphore(5) #添加一个计数器
def foo():
s1.acquire() #计数器获得锁
time.sleep(2) #程序休眠2秒
print("ok",time.ctime())
s1.release() #计数器释放锁 for i in range(20):
t1=threading.Thread(target=foo,args=()) #创建线程
t1.start() #启动线程
或者使用此方法
import time
import threadingsem=threading.Semaphore(5) #添加一个计数器
def foo():
with sem:
time.sleep(2) #程序休眠2秒
print("ok",time.ctime())for i in range(20):
t1=threading.Thread(target=foo,args=()) #创建线程
t1.start() #启动线程