1.线程
什么是线程
线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的执行流程
2.线程与进程的关系
线程不能单独存在必须存在于进程中,进程是一个资源单位,其包含了运行程序所需的所有资源,线程才是真正的执行单位
没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程,当我们启动一程序时,操作系统就会为自己这个程序创建一个主线程,线程可以由程序后期开启,自己开启线程称之为子线程
3.为什么需要线程
目的只有一个就是提高效率
4.多线程的使用方式
继承Thread类,然后重写run()方法
from threading import Thread,current_thread import time def task(): print("2",current_thread()) print("子进程running") time.sleep(10) print("子进程") if __name__ == '__main__': t=Thread(target=task) t.start() print("主线程") print("1",current_thread()) class myThread(Thread): def run(self): print("子进程tun") m=myThread() print("父进程")
5.线程的特点
1)创建开销小
2)同一个进程中的多个线程数据可以共享
3)多线程之间,是平等关系,没有父子关系,所有的线程的PID都是相同的
# 创建线程开销对比 import os from threading import Thread from multiprocessing import Process import time def task(): # print("hello") print(os.getpid()) pass if __name__ == '__main__': st_time = time.time() ts = [] for i in range(100): t = Thread(target=task) # t = Process(target=task) t.start() ts.append(t) for t in ts: t.join() print(time.time()-st_time) print("主over")
6.守护线程
一个线程可以设置为另一个线程的守护线程
特点: 被守护线程结束后守护线程也随之结束
from threading import Thread import time def task(): print("子1running......") time.sleep(100) print("子1over......") def task2(): print("子2running......") time.sleep(4) print("子2over......") t = Thread(target=task) t.daemon = True t.start() t2 =Thread(target=task2) t2.start() print("主over")
共享意味着竞争
线程中也存在安全问题,
多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题
解决方案:还是互斥锁
from threading import Thread,enumerate,Lock import time number = 10 lock = Lock() def task(): global number lock.acquire() a = number time.sleep(0.1) number = a - 1 lock.release() for i in range(10): t = Thread(target=task) t.start() for t in enumerate()[1:]: # print(t) t.join() print(number)
from threading import Lock, current_thread, Thread """ 死锁问题 当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源 要想使用必须同时具备两把锁 这时候程序就会进程无限卡死状态 ,这就称之为死锁 例如: 要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子 如何避免死锁问题 锁不要有多个,一个足够 如果真的发生了死锁问题,必须迫使一方先交出锁 """ import time # 盘子 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()
Rlock 称之为递归锁或者可重入锁
与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程
已经执行过acquire ,其他线程将无法执行acquire
from threading import RLock, Lock, Thread # l = Lock() # # l.acquire() # print("1") # l.acquire() # print("2") l = RLock() # l.acquire() # print("1") # l.acquire() # print("2") def task(): l.acquire() print("子run......") l.release() # 主线程锁了一次 l.acquire() l.acquire() l.release() l.release() t1 = Thread(target=task) t1.start()
""" 信号量 了解 Lock RLock 可以现在被锁定的代码 同时可以被多少线程并发访问 Lock 锁住一个马桶 同时只能有一个 Semaphore 锁住一个公共厕所 同时可以来一堆人 用途: 仅用于控制并发访问 并不能防止并发修改造成的问题 """ 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()