线程的特点
线程是程序运行中的最基本单元,在一个进程内有且至少有一个进程。
进程:内存独立,CPU独立,速率低共享数据难,安全性高
线程:内存共享,CPU使用独立,是cpu最小的执行单位
线程的创建方法
1.直接实例化Thread类
t = Thread(target=task) t.start() task() #执行顺序不固定 如果开启线程速度足够快 可能子线程先执行 print("主线程over") print("1",current_thread())
2.创建类覆盖Thread中的run方法
class MyThread(Thread): def run(self): print("子线程run!") m = MyThread() m.start() print("主线over")
使用方法和进程一样,但是开启线程的代码可以放在任何位置,开启进程的方法只能放在__name__==__main__中
在同一进程中线程间数据共享
from threading import Thread a = 10 def task(): global a print("子 running...") a = 20 t1 = Thread(target=task) t1.start() t1.join() # 主线程等待子线执行完毕 print(a) ==》 子 running... 20
进程与线程效率
开启一百个进程
def task(): pass if __name__ == '__main__': st_time=time.time() for a in range(100): t=Process(target=task) t.start() print(time.time()-st_time) ==》 3.447035074234009 开启一百个进程花费的时间
开启一百个线程
def task(): pass if __name__ == '__main__': st_time = time.time() for a in range(100): t=Thread(target=task) t.start() print(time.time()-st_time) print('over') ==》 0.014977693557739258
由此可见线程的开销远远小于进程的开销
守护线程
def task():
print('1run')
time.sleep(3)
print('1over')
def task1():
print('2run')
time.sleep(6)
print('2over')
t=Thread(target=task)
t1=Thread(target=task1)
t.start()
t1.daemon=True
t1.start()
print('over')
==>
1run
2run
over
1over
主线程代码执行完后不会立即结束,会等待其他非守护进程
死锁
当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源要想使用必须同时具备两把锁这时候程序就会进程无限卡死状态 ,这就称之为死锁
lock1 = Lock() # 筷子 lock2 = Lock() def eat1(): lock1.acquire() print("%s抢到了盘子" % current_thread().name) time.sleep(0.5) lock2.acquire() print("%s抢到了筷子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) def eat2(): lock2.acquire() print("%s抢到了筷子" % current_thread().name)
lock1.acquire() print("%s抢到了盘子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) t1 = Thread(target=eat1) t2 = Thread(target=eat2) t1.start() t2.start() ==》 Thread-1抢到了盘子 Thread-2抢到了筷子
如上所示,两个锁可以看作两个许可,线程需要同时拿到两把锁才可以正常运行,当两个线程各拿到一把锁的时候双方都无法正常进行下去,这就出现了死锁
如何避免死锁问题
锁不要有多个,一个足够
如果真的发生了死锁问题,必须迫使一方先交出锁
Rlock
称之为递归锁或者可重入锁,Rlock不使用用来解决死锁问题的
与Lock唯一的区别:
Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次
如果一个线程已经执行过acquire 其他线程将无法执行acquire
信号量
可以现在被锁定的代码 同时可以被多少线程并发访问
用途: 仅用于控制并发访问 并不能防止并发修改造成的问题
from threading import Semaphore, Thread import time s = Semaphore(5) def task(): s.acquire() print("子run") time.sleep(3) print("子over") s.release() for i in range(10): t = Thread(target=task) t.start()