什么是线程?
py文件在执行程序中,他会根据程序的编写来区分,假如没有创建子进程,整个程序就是主进程。
那程序中,有主线程而且还有子线程,那他就是一个多线程。
使用多线程可以提升I/O密集型的效率。
什么是进程?
py文件就是一个进程,比如:QQ,360,浏览器。
使用多进程,会消耗很大的资源问题。
GIL锁
GIL锁又称,全局解释器锁。
GIL锁的作用:在同一时刻,只能有一个线程进入解释器。
站在开发Python语言的那一端时,他就是一个神器,而站在使用这门语言这一端时,他就是一个BUG般的存在,而这BUG仅存在于CPython中。
为什么说BUG呢,因为有了GIL锁,我们使用多线程在进行计算密集型中,计算机的核数再多,他也只能使用一核。
I/O密集型,计算密集型
什么是I/O密集型?
说白了,他就是一个堵塞,当我们创建多线程(A、B),A线程在执行,遇到了堵塞,在CPU空闲时,切换到了B线程。
import threading import time start = time.time() def music(): print('I listening Music') time.sleep(2) def movie(): print('I watching TV') time.sleep(3) t1 = threading.Thread(target=music) t2 = threading.Thread(target=movie) t1.start() t2.start() end = time.time() result = end - start print(result) print('主线程结束')
什么时计算密集型?
线程在计算过程中,没有遇到堵塞,而是一直在执行计算。
def add(): num = 0 for i in range(1000000): num += i print(num)
如何创建多线程?
Threading模块
函数创建
import threading import time start = time.time() def music(): for i in range(3): print('I listening Music') time.sleep(1) def movie(): for i in range(2): print('I watching TV') time.sleep(5) t1 = threading.Thread(target=music) #创建子线程 t2 = threading.Thread(target=movie) #创建子线程 threads = [t1,t2] for t in threads: t.start() #启动子线程
类创建
import threading class MyThread(threading.Thread): # 首先要继承这个方法 def __init__(self,count): super().__init__() self.count = count def current_thread_count(self): print(self.count) def run(self): #定义每个线程要运行的内容 self.current_thread_count() t1 = MyThread(threading.active_count()) t2 = MyThread(threading.active_count()) t1.start() #开启线程 t2.start()
join ()方法
主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,
才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。join([timeout]) 里面的参数时可选的,代表线程运行的最大时
间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的,如果线程执行时间小于参数表示的
时间,则接着执行,不用一定要等待到参数表示的时间。
import threading import time start = time.time() def music(): for i in range(3): print('I listening Music') time.sleep(1) def movie(): for i in range(2): print('I watching TV') time.sleep(5) t1 = threading.Thread(target=music) #创建子线程 t2 = threading.Thread(target=movie) #创建子线程 threads = [t1,t2] for t in threads: t.start() #启动子线程 t.join() # 代表赋值前的一个,也就是t2 print('主线程结束')
setDaemon()方法
主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这
时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有
个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起,只有等待了所有线程结束它才结束。
import threading import time start = time.time() def music(): for i in range(3): print('I listening Music') time.sleep(1) def movie(): for i in range(2): print('I watching TV') time.sleep(5) t1 = threading.Thread(target=music) #创建子线程 t2 = threading.Thread(target=movie) #创建子线程 threads = [t1,t2] t2.setDaemon(1) #守护线程 for t in threads: t.start() #启动子线程 print('主线程结束')
同步锁
为什么会有同步锁?
当我们创建多线程时,并且有一个全局变量,而多线程操作这个全局变量。
import threading import time def sub(): global number num = number time.sleep(0.1) number = num - 1 number = 10 threads = [] for i in range(10): t = threading.Thread(target=sub) t.start() threads.append(t) for i in threads: i.join() print(number) # 9
结果并不是我们想要的。
为什么出现这种问题?
程序在sleep的一瞬间,cpu来回切换,还没等着修改全局变量,所有的线程已经被创建,而且也已经被赋值。
如何解决?
那就是加锁了。
import threading import time def sub(): global number r.acquire() # 获得锁 num = number time.sleep(0.1) number = num - 1 r.release() # 释放锁 number = 10 threads = [] r = threading.Lock() for i in range(10): t = threading.Thread(target=sub) t.start() threads.append(t) for i in threads: i.join() print(number) # 0
加锁,其实就是不让cup进行线程切换,直到锁被释放。
如果锁没被释放,不会让其他线程进入,也不会影响不进入线程的执行。
import threading import time number = 10 threads = [] r = threading.Lock() def sub(): global number r.acquire() num = number time.sleep(0.1) number = num - 1 r.release() def music(): time.sleep(0.5) print('Music') t = threading.Thread(target=music) t.start() for i in range(10): t = threading.Thread(target=sub) t.start() threads.append(t) for i in threads: i.join() print(number)
递归锁(Rlock)
import threading r = threading.Lock() class MyThread(threading.Thread): def Thread_1(self): r.acquire() print('第一层',self.name) r.acquire() print('第二层',self.name) r.release() r.release() def run(self): self.Thread_1() for i in range(5): t = MyThread() t.start()
递归锁,与Lock很相似,但是他有一个计数的功能,能解决死锁
import threading r = threading.RLock() class MyThread(threading.Thread): def Thread_1(self): r.acquire() print('第一层',self.name) r.acquire() print('第二层',self.name) r.release() r.release() def Thread_2(self): r.acquire() print('第一层',self.name) r.acquire() print('第二层',self.name) r.release() r.release() def run(self): self.Thread_1() self.Thread_2() for i in range(5): t = MyThread() t.start()
信号量(Semaphore)
信号量相当于,可以限制最大进入的线程数量。
import threading import time r = threading.Semaphore(2) # 创建信号量,最大进入的线程数量 class MyThread(threading.Thread): def Thread_1(self): r.acquire() # 每次进入线程+1,但不能超过信号量设定的值 print(self.name) time.sleep(2) r.release() # -1 def run(self): self.Thread_1() for i in range(5): t = MyThread() t.start()
条件变量(Condition)
wait():条件不满足时调用,线程会释放锁并进入等待阻塞。
notify():条件创造后调用,通知等待池激活一个线程。
notifyAll():条件创造后调用,通知等待池激活所有线程。
import threading import time import random def producer(): time.sleep(0.2) global F while True: if con_Lock.acquire(): r = random.randrange(0,100) F.append(r) print(str(threading.current_thread())+ '--->' + str(r)) con_Lock.notify() con_Lock.release() time.sleep(3) def consumer(): global F while True: con_Lock.acquire() if not F: print("老板,快点,没有包子了") con_Lock.wait() a = F.pop() print('包子%s已经被吃'%a) time.sleep(0.5) con_Lock = threading.Condition() threads = [] F = [] for i in range(5): threads.append(producer) threads.append(consumer) for i in threads: t = threading.Thread(target=i) t.start()
import threading import time con = threading.Condition() num = 0 # 生产者 class Producer(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): # 锁定线程 global num con.acquire() while True: print "开始添加!!!" num += 1 print "火锅里面鱼丸个数:%s" % str(num) time.sleep(1) if num >= 5: print "火锅里面里面鱼丸数量已经到达5个,无法添加了!" # 唤醒等待的线程 con.notify() # 唤醒小伙伴开吃啦 # 等待通知 con.wait() # 释放锁 con.release() # 消费者 class Consumers(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): con.acquire() global num while True: print "开始吃啦!!!" num -= 1 print "火锅里面剩余鱼丸数量:%s" %str(num) time.sleep(2) if num <= 0: print "锅底没货了,赶紧加鱼丸吧!" con.notify() # 唤醒其它线程 # 等待通知 con.wait() con.release() p = Producer() c = Consumers() p.start() c.start()
Event
from threading import Event Event.isSet() #返回event的状态值 Event.wait() #如果 event.isSet()==False将阻塞线程; Event.set() #设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; Event.clear() #恢复
实例
#首先定义两个函数,一个是连接数据库 # 一个是检测数据库 from threading import Thread,Event,currentThread import time e = Event() def conn_mysql(): '''链接数据库''' count = 1 while not e.is_set(): #当没有检测到时候 if count >3: #如果尝试次数大于3,就主动抛异常 raise ConnectionError('尝试链接的次数过多') print('