线程
什么是线程
- 在程序运行过程中,执行程序代码的一个分支,每个运行的程序至少都有一个线程
- 线程列表:
threading.enumerate()
import threading
import time
def song(name, age):
while True:
print('song', name, age)
time.sleep(1)
def dance():
while True:
print('dance')
time.sleep(1)
def main():
# child_thread = threading.Thread(target=dance)
# child_thread.start()
# 可以转换成:
threading.Thread(target=dance).start()
# 传参 ==>> args=() 传入的是一个元组
threading.Thread(target=song, args=('小张', 14)).start()
# 创建两个进程,并开启进程
if __name__ == '__main__':
main()
注意点
- 线程之间的执行是无序的
- 主线程会等待所有的子线程结束后才结束
- 子线程任务执行不完程序退出不了,解决办法设置子线程守护主线程
- 守护主线程
- 设置成为守护主线程,主线程退出后子线程直接销毁, 不再执行子线程的代码
daemon=True 守护主线程
import threading
import time
# 测试主线程是否会等待子线程执行完成以后程序再退出
def show_info():
for i in range(5):
print("test:", i)
time.sleep(0.5)
if __name__ == '__main__':
# 创建子线程守护主线程
# daemon=True 守护主线程
# 守护主线程方式1
sub_thread = threading.Thread(target=show_info, daemon=True)
# 设置成为守护主线程,主线程退出后子线程直接销毁不再执行子线程的代码
# 守护主线程方式2
# sub_thread.setDaemon(True)
sub_thread.start()
# 主线程延时1秒
time.sleep(1)
print("over")
自定义线程
不能指定target
,因为自定义线程里面的任务都统一在run方法里面执行
启动统一调用start方法(直接run, 不是使用子线程去执行任务)
import threading
# 自定义线程类
class MyThread(threading.Thread):
# 通过构造方法取接收任务的参数
def __init__(self, info1, info2):
# 调用父类的构造方法
super(MyThread, self).__init__()
self.info1 = info1
self.info2 = info2
# 定义自定义线程相关的任务
def test1(self):
print(self.info1)
def test2(self):
print(self.info2)
# 通过run方法执行相关任务
def run(self):
self.test1()
self.test2()
# 创建自定义线程
my_thread = MyThread("测试1", "测试2")
# 启动
my_thread.start()
##共享全局变量
多线程共享全局变量,很方便在多个线程间共享数据
多线程同时操作全局变量(资源竞争), 导致数据可能出现错误
- 线程同步: 保证同一时刻只能有一个线程去操作全局变量(
变成单任务
)- 线程等待(
join
) - 互斥锁
- 线程等待(
- 同步: 就是协同步调,按预定的先后次序进行运行。
import threading
# 定义全局变量
g_num = 0
# 循环1000000次每次给全局变量加1
def sum_num1():
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 循环1000000次每次给全局变量加1
def sum_num2():
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
if __name__ == '__main__':
# 创建两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 启动线程
first_thread.start()
# 主线程等待第一个线程执行完成以后代码再继续执行,让其执行第二个线程
# 线程同步: 一个任务执行完成以后另外一个任务才能执行,同一个时刻只有一个任务在执行
first_thread.join()
# 启动线程
second_thread.start()
互斥锁
- 对共享数据进行锁定,保证同一时刻只能有一个线程去操作
- 如果这个锁之前是没有上锁的,那么acquire不会堵塞
- 如果在调用acquire对这个锁上锁之前,它已被其他线程上锁,则此时acquire会堵塞,直到这个锁被解锁为止
能够保证多个线程访问共享数据不会出现资源竞争及数据错误
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
注意
- 抢到锁的线程先执行,没有抢到锁的线程需要等待,等锁用完后需要释放,然后其它等待的线程再去抢这个锁,那个线程抢到那个线程再执行。
- 具体那个线程抢到这个锁我们决定不了,是由cpu调度决定的
优缺点
- 优点
- 确保了某段关键代码只能由一个线程从头到尾完整地执行
- 缺点
- 多线程执行变成了包含锁的某段代码
实际上只能以单线程模式执行
,效率就大大地下降了 - 锁使用不好就容易出现
死锁
情况
- 多线程执行变成了包含锁的某段代码
死锁
- 一直等待对方释放锁的情景就是死锁
- 死锁一旦发生就会造成应用的停止响应
避免死锁
:在合适的地方释放锁
Django CreateView、UpdateView 源码解析
Django WebSocket (一):WebSocket 概念及原理
Django WebSocket (二):Channels 概念及原理
Django WebSocket (三):Channels + WebSocket 实现在线聊天
GitLab、Jenkins、Harbor 安装 | CentOS、Docker
JetBrains | PyCharm 链接 GitLab | GitHub
评论区