1.线程
线程是操作系统资源调度的最小单位,一个进程最少有一个线程。同一个进程的线程可以资源共享。
前台线程(默认):主线程执行结束后会等待子线程结束完后一起退出释放资源。
后台线程:主线程执行结束就退出。
# 设置前台线程
t.setDaemon(False)
# 设置后台线程
t.setDaemon(True)
2.开启5个线程访问百度
import threading
import time
import requests
def get_content(url):
result = requests.get(url).text
time.sleep(0.5)
print("get url")
# 计时装饰器
def runtime(func):
def inner(*args, **kwargs):
print("start.......")
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"花了{end_time-start_time}s")
return result
return inner
@runtime
def main():
for i in range(5):
# target必须是一个callable对象
target = threading.Thread(target=get_content, args=("http://www.baidu.com",))
target.start()
main()
根据运行结果显示
只花了0.003秒,这就是多线程的好处,如果不使用多线程,我们的程序只能顺序执行,所以整个程序执行结束至少需要2.5s,使用多线程就大大降低了我们的时间消耗。主线程只需要告诉子线程去访问百度,至于子线程怎么去访问百度主线程是不关心的,主线程只是一个发号施令者。
但是在工作场景下,可以加一个阻塞模式,阻塞当前上下文,等子线程结束以后一起退出
import threading
import time
import requests
def get_content(url):
result = requests.get(url).text
time.sleep(0.5)
print("get url")
# 计时装饰器
def runtime(func):
def inner(*args, **kwargs):
print("start.......")
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"花了{end_time-start_time}s")
return result
return inner
@runtime
def main():
lst = []
for i in range(5):
target = threading.Thread(target=get_content, args=("http://www.baidu.com",))
target.start()
lst.append(target)
for target in lst:
target.join()
main()
等在子线程一起结束的结果是,很显然多线程的效率还是比顺序执行高了很多。
3.自定义线程类
from threading import Thread
# 自定义线程类:采用继承的方式自定义类
class MyThread(Thread):
def __init__(self, num):
# 重写init函数,super()重新执行父类的init函数
super().__init__()
self.num = num
def run(self):
print(f"running...{self.num}")
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
4.线程锁
由于同一个进程内的线程是资源共享的,但是资源紧缺的时候,线程间就会产生资源抢夺问题,进而产生意想不到的脏数据(错误数据)。
4.1 互斥锁:允许多个线程进行资源抢夺,但是同一时刻只能由一个线程可以访问资源。
(提示:互斥锁最好在有资源抢夺的时候再用,如果全程加锁多线程会失去效果)
Lock 原始锁:在获取锁之前一直处于等待状态,直到获取到锁以后才往下执行
RLock 重入锁:在获取锁之前判断是否已经获取到锁,如果没有获取到锁就处于等待状态,如果已经获取到锁就往下执行。
(建议:在我们的开发环境中最好还是使用重入锁,原始锁可能会在我们不想要地方卡住,导致程序无法继续执行)
import threading
lock1 = threading.Lock()
lock2 = threading.RLock()
lock1.acquire()
print("lock1 acquired 1")
lock1.acquire()
print("lock1 acquired 1 again")
lock1.release()
print("lock1 release 1")
lock1.release()
print("lock1 release 1 again")
# lock1 acquired 1
# 产生死锁
lock2.acquire()
print("lock2 acquired 2")
lock2.acquire()
print("lock2 acquired 2 again")
lock2.release()
print("lock2 release 2")
lock2.release()
print("lock2 release 2 again")
# lock2 acquired 2
# lock2 acquired 2 again
# lock2 release 2
# lock2 release 2 again
原始锁使用方法,重入锁只是将Lock改成RLock
from threading import Lock
# 创建一个锁实例
lock = Lock()
# 在要加锁的部分的开始加上
lock.acquire()
# 在锁的末尾加上
lock.release()
4.2信号量:设置同一时刻最多有几个线程可以访问公共资源
4.3事件锁:根绝状态,确定是否通过事件,主线程控制其他线程是否可以访问系统资源
4.4条件锁:事件锁和信号量的结合,当满足条件时,控制n个线程可以访问公共资源。