有些事情不是难以做到才失去信心,而是因为失去信心才难以做到。 ——肖乾旭
线程
1、线程的介绍
在Python中,想要实现多任务除了使用进程,还可以使用线程来完成,线程是实现多任务的另外一种方式。
2、线程的概念
线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度,也就是说线程是cpu调度的基本单位,每个进程至少有一个线程,而这个线程就是我们常说的主线程。
3、线程的作用
多线程可以完成多任务
多线程效果图:
说明:程序启动默认会有一个主线程,程序员自己创建的线程可以成为子线程,多线程可以完成多任务。
4、小结
多线程的使用
1、导入线程模块
# 导入线程模块
import threading
2、线程类Thread参数说明
Thread([group[,target[,name[,args[,kwargs]]]]])
3、启动线程
启动线程使用start方法
4、多线程完成多任务的代码
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 8:29 下午
# @Author : 李明辉
# @File : ithui_多线程的使用.py
# @Software : PyCharm
# 1、导入线程模块
import threading
import time
def sing():
# 获取当前线程
current_thread = threading.current_thread()
print("sing:", current_thread)
for i in range(3):
print('唱歌中...')
time.sleep(0.2)
def dancee():
# 获取当前线程
current_thread = threading.current_thread()
print("dance:", current_thread)
for i in range(3):
print('跳舞中...')
time.sleep(0.2)
if __name__ == '__main__':
# 获取当前线程
current_thread = threading.current_thread()
print("main_thread:", current_thread)
# 2、创建子进程
sing_thread = threading.Thread(target=sing)
dance_thread = threading.Thread(target=dancee)
# 3、启动子线程执行对应的任务
sing_thread.start()
dance_thread.start()
运行结果:
线程执行带有参数的任务
1、线程执行带有参数的任务的介绍
Thread类执行任务并给任务传参数的方式有两种:
2、args参数的使用
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 8:44 下午
# @Author : 李明辉
# @File : ithui_线程执行带有参数的任务.py
# @Software : PyCharm
import threading
def show_info(name, age):
print("name: %s age: %d" % (name, age))
if __name__ == '__main__':
# 创建子线程
# 以元组方式传参,保证元组里面元素的顺序和函数的参数顺序一样
sub_thread = threading.Thread(target=show_info, args=("李四",20))
# 启动线程
sub_thread.start()
结果如下:
3、kwargs参数的使用:
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 8:44 下午
# @Author : 李明辉
# @File : ithui_线程执行带有参数的任务.py
# @Software : PyCharm
import threading
def show_info(name, age):
print("name: %s age: %d" % (name, age))
if __name__ == '__main__':
# 创建子线程
# 以元组方式传参,保证元组里面元素的顺序和函数的参数顺序一样
# sub_thread = threading.Thread(target=show_info, args=("李四",20))
# 启动线程
# sub_thread.start()
sub_thread = threading.Thread(target=show_info, kwargs={"name":"王五","age":20})
sub_thread.start()
结果如下:
线程的注意点
1、线程的注意点介绍
2、线程之间执行是无序的
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 8:54 下午
# @Author : 李明辉
# @File : ithui_线程之间执行是无序的.py
# @Software : PyCharm
import threading
import time
def task():
time.sleep(1)
# 获取当前线程
print(threading.current_thread())
if __name__ == '__main__':
# 循环创建大量线程,测试线程之间的执行是否无序
for i in range(20):
# 每循环一次,创建一个子线程
sub_thread = threading.Thread(target=task)
# 启动子线程执行对应的任务
sub_thread.start()
结果如下:
3、主线程会等待所有的子线程执行结束再结束
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 9:02 下午
# @Author : 李明辉
# @File : ithui_主线程会等待所有的子线程执行结束再结束.py
# @Software : PyCharm
import threading
import time
def task():
while True:
print("任务执行中...")
time.sleep(0.3)
if __name__ == '__main__':
# daemon=True表示创建的子线程守护主线程,主线程退出,子线程直接销毁
sub_thread = threading.Thread(target=task, daemon=True)
sub_thread.start()
# 主线程延时执行1秒
time.sleep(1)
print("over")
# 结论:主线程会等待子线程执行结束再结束
结果如下:
3、线程之间共享全局变量
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 9:10 下午
# @Author : 李明辉
# @File : ithui_线程之间共享全局变量.py
# @Software : PyCharm
import threading
g_list = []
def add_data():
for i in range(3):
g_list.append(i)
print("add:", i)
print("添加数据完成", g_list)
def read_data():
print(g_list)
if __name__ == '__main__':
add_thread = threading.Thread(target=add_data)
read_thread = threading.Thread(target=read_data)
add_thread.start()
read_thread.start()
结果如下:
4、线程之间共享全局数据出现错误问题
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 9:18 下午
# @Author : 李明辉
# @File : ithui_线程之间共享全局数据出现错误问题.py
# @Software : PyCharm
import threading
g_num = 0
def task1():
for i in range(1000000):
global g_num # 表示要声明修改全局变量的内存地址
g_num += 1
print("task1", g_num)
def task2():
for i in range(1000000):
global g_num # 表示要声明修改全局变量的内存地址
g_num += 1
print("task2", g_num)
if __name__ == '__main__':
first_thread = threading.Thread(target=task1)
second_thread = threading.Thread(target=task2)
first_thread.start()
second_thread.start()
结果如下:
错误分析:
两个线程first_thread和second_thread都要对全局变量g_num(默认是0)进行加1运算,但是由于是多线程时操作,有可能出现下面情况:
全局变量数据错误的解决办法:
线程同步:保证同一时刻只能有一个线程去操作全局变量 同步:就是协同步调,按预定的先后次序进行运行,如:你说完,我再说好,好比如现实生活中的对讲机。
线程同步的方式:
互斥锁
1、互斥锁的概念
互斥锁:对共享数据进行锁定,保证同一时刻只能有一个线程去操作。
注意:
2、互斥锁的使用
threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。
互斥锁使用步骤:
# 创建锁
mutex = threading.lock()
# 上锁
mutex.acquire()
…这里编写代码能保证同一时刻只能有一个线程去操作,对共享数据进行锁定…
# 释放锁
mutex.release()
注意点:
3、使用互斥锁完成两个线程同时对一个全局变量各加100万次的操作
代码演示:
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 9:53 下午
# @Author : 李明辉
# @File : ithui_互斥锁.py
# @Software : PyCharm
# -*- codeing = utf-8 -*-
# @Time : 2021/12/7 9:18 下午
# @Author : 李明辉
# @File : ithui_线程之间共享全局数据出现错误问题.py
# @Software : PyCharm
import threading
g_num = 0
# 创建互斥锁
lock = threading.Lock()
def task1():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num # 表示要声明修改全局变量的内存地址
g_num += 1
print("task1", g_num)
# 释放
lock.release()
def task2():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num # 表示要声明修改全局变量的内存地址
g_num += 1
print("task2", g_num)
# 释放
lock.release()
if __name__ == '__main__':
first_thread = threading.Thread(target=task1)
second_thread = threading.Thread(target=task2)
first_thread.start()
second_thread.start()
结果如下:
说明:
通过执行结果可以保证互斥锁能够保证多个线程访问共享数据不会出现数据错误问题
4、小结
死锁
1、死锁的概念
死锁:一直等待对方释放锁的情节就是死锁
说明:现实生活中,男女双方一直等待 对方先道歉的这种行为就好比是死锁。
进程和线程的对比
1、进程和线程的对比的三个方向
2、关系对比
3、区别对比
4、优缺点对比
5、小结
如果前面还不太了解的朋友可以看看《多任务编程 - 1》哦
END