线程 可以理解为在进程中独立运行的子程序,是操作系统能够运行调度的最小单位。
举个不是特别恰当的例子,比如你正在听歌,然后你突然发现一首歌特别好听,你就想下载下来,这个时候你想边听边下载,但是这个时候就有一个尴尬的事情,如果你只有一个进程,会造成你在下载的时候不能听歌;如果有多个进程,进程A负责听,进程B负责下载。这时候又会出现两个进程协作的问题,进程执行过程中拥有独立的内存单元,彼此间资源不共享,不停地切换进程会造成性能上的损伤。这时候,如果能有一个机制,可以使两个任务共享资源,这样就能解决进程通信问题,降低来回切换的损耗,那就非常好了。这,就是线程。
线程特点:
1. 一个进程至少有一个线程;
2. 多个线程共享内存;
3. 线程不能够独立执行,必须依存于进程
在CPython中,全局解释器锁(GIL)是一个互斥锁,它可以防止多个本机线程同时执行Python字节码。这个锁是必需的,主要是因为CPython的内存管理不是线程安全的。(然而,由于GIL的存在,其他功能已经依赖于它所执行的保证。)
这就意味着:无论你启多少个线程,你有多少个cpu, Python在执行的时候在同一时刻只允许一个线程运行。
import threading #线程
import time
def Hi(num):
print("hello %d" %num)
time.sleep(3)
print("world")
if __name__ == '__main__':
t1=threading.Thread(target=Hi,args=(10,)) #创建一个线程对象
t2=threading.Thread(target=Hi,args=(9,)) #创建一个线程对象
t1.start() #启动线程
t2.start() #启动另一个线程
print("ending.........")
执行结果:
hello 10
hello 9
ending.........
(将在此处停留三秒)
world
world
import threading
import time
class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self) #最好采用super()
self.num = num
def run(self):#定义每个线程要运行的函数
print("running on number:%s" %self.num)
time.sleep(3)
if __name__ == '__main__':
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
print("ending......")
自定义一个类,继承 threading.Thread,然后重新 run 方法
执行结果:
running on number:1
running on number:2
ending......
(停留三秒,然后结束运行)
import threading
import time
def music():
print("begin to listen %s"%time.ctime())
time.sleep(3)
print("stop to listen %s" % time.ctime())
def game():
print("begin to play game %s" % time.ctime())
time.sleep(5)
print("stop to play game %s" % time.ctime())
if __name__ == '__main__':
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
t2.setDaemon(True)
# t1.setDaemon(True)
t1.start()
t2.start()
t1.join()
# print(threading.currentThread())
# print(threading.enumerate())
# print(threading.activeCount())
print("ending....")
猜想运行结果:
begin to listen Wed Jul 22 23:26:21 2020
begin to play game Wed Jul 22 23:26:21 2020
ending....
stop to listen Wed Jul 22 23:26:24 2020
stop to play game Wed Jul 22 23:26:26 2020
实际运行结果:
begin to listen Wed Jul 22 23:26:21 2020
begin to play game Wed Jul 22 23:26:21 2020
stop to listen Wed Jul 22 23:26:24 2020
ending....
为什么与设想不符?
这便是join()&Daemon方法
join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
setDaemon(True):
将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。被设置为守护线程的线程,当主线程结束时,守护线程如果还没有结束,将会随主线程直接结束
# run(): 线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
当多个线程线程都在同时操作一个共享资源,可能会造成资源破坏可使用同步锁解决问题
import threading, time
def sub():
global num #在每个线程都获得这个共享量
lock.acquire()
temp=num
time.sleep(0.0004)
num=temp-1
lock.release()
if __name__ == "__main__":
num = 100 #设置一个共享量
l = []
lock = threading.Lock() #实例化出一个同步锁
for i in range(100):
t = threading.Thread(target=sub)
t.start()
l.append(t)
for i in l:
i.join()
print(num)
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。
此时可使用递归锁
import threading
import time
class MyThread(threading.Thread):
def actionA(self):
r_lcok.acquire() #count=1
print(self.name,"gotA",time.ctime())
time.sleep(2)
r_lcok.acquire() #count=2
print(self.name, "gotB", time.ctime())
time.sleep(1)
r_lcok.release() #count=1
r_lcok.release() #count=0
def actionB(self):
r_lcok.acquire()
print(self.name, "gotB", time.ctime())
time.sleep(2)
r_lcok.acquire()
print(self.name, "gotA", time.ctime())
time.sleep(1)
r_lcok.release()
r_lcok.release()
def run(self):
self.actionA()
self.actionB()
if __name__ == '__main__':
# A=threading.Lock()
# B=threading.Lock()
r_lcok=threading.RLock() #获得一个递归锁对象
L=[]
for i in range(5):
t=MyThread()
t.start()
L.append(t)
for i in L:
i.join()
print("ending....")
递归锁相当于内部有一个计数器
acquire一次,计数器+1
release一次,计数器-1
只要count>1,内存不释放
event是简单的同步对象;event表示内部标志,
线程可以等待标志被设置,或者自己设置或清除标志。
import threading, time
class Boss(threading.Thread):
def run(self):
print("BOSS:今晚大家都要加班到22:00。")
print(event.isSet())# False
time.sleep(4)
event.set()
time.sleep(5)
print("BOSS:<22:00>可以下班了。")
print(event.isSet())
event.set()
class Worker(threading.Thread):
def run(self):
event.wait() # 一旦event被设定,等同于pass
print("Worker:哎……命苦啊!")
time.sleep(1)
event.clear()
event.wait()
print("Worker:OhYeah!")
if __name__=="__main__":
event = threading.Event()
threads = []
for i in range(5):
threads.append(Worker())
threads.append(Boss())
for t in threads:
t.start()
for t in threads:
t.join()
print("ending.....")
信号量用来控制线程并发数的
import threading,time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(3)
semaphore.release()
if __name__=="__main__":
semaphore = threading.Semaphore(25)
thrs = []
for i in range(100):
thrs.append(myThread())
for t in thrs:
t.start()
import threading,time
li=[1,2,3,4]
def pri():
while li:
a=li[-1]
print(a)
time.sleep(1)
li.remove(a)
# try:
# li.remove(a)
# except Exception as e:
# print('----',a,e)
t1=threading.Thread(target=pri,args=())
t1.start()
t2=threading.Thread(target=pri,args=())
t2.start()
import queue
q = queue.Queue(3)
q.put(1)
q.put("hello")
q.put("world")
print(q.full())
# q.put(233,False)
while 1:
# data = q.get()
# print(data)
# print("----------")
# data = q.get()
data = q.get(False)
print(data)
print("----------")
if q.empty():
break
print("ending-----")
创建一个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
将一个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
将一个值从队列中取出
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize)
此包中的常用方法(q = Queue.Queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作
import time,random
import queue,threading
q = queue.Queue()
def Producer(name):
count = 0
while count <10:
print("making........")
time.sleep(5)
q.put(count)
print('Producer %s has produced %s baozi..' %(name, count))
count +=1
# q.task_done()
q.join()
print("ok......")
def Consumer(name):
count = 0
while count <10:
time.sleep(random.randrange(4))
# if not q.empty():
print("waiting.....")
# q.join()
data = q.get()
print("eating....")
time.sleep(4)
q.task_done()
#print(data)
print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
# else:
# print("-----no baozi anymore----")
count +=1
p1 = threading.Thread(target=Producer, args=('A君',))
c1 = threading.Thread(target=Consumer, args=('B君',))
c2 = threading.Thread(target=Consumer, args=('C君',))
c3 = threading.Thread(target=Consumer, args=('D君',))
p1.start()
c1.start()
c2.start()
c3.start()