1.线程的介绍
-
在Python中,想要实现多任务除了使用进程,还可以使用线程来完成,线程是实现多任务的另外一种方式。
-
线程的概念:线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是主线程。
-
线程的作用:多线程可以在同一片内存空间完成多任务
2.线程创建与多线程
-
导入线程模块
# 导入线程模块
import threading
-
线程类Thread ——创建线程与参数说明
def func(data): return # 以元组的方式给执行任务传参 t1 = threading.Thread(target=func,args=('python',),name='t1') # 以字典方式给执行任务传参 t2 = threading.Thread(target=func,kwargs={'data':'itcast'},name='t2')
-
格式:Thread(group , target , name , args , kwargs])
-
group: 线程组,目前只能使用None
-
target: 执行的目标任务名
-
线程参数:
-
args: 以元组的方式给执行任务传参
-
kwargs: 以字典方式给执行任务传参
-
-
name: 线程名,一般不用设置
-
daemon:设置守护线程
-
t1.start():启动线程执行任务
-
threading.current_thread().name:获取当前执行线程的名称
-
创建线程与启动完整代码展示:
# 导入线程模块
import threading
def func(data):
print('线程使用')
print(f'参数数据:{data}')
# 获取当前执行线程的名称
print('线程名称:',threading.current_thread().name)
if __name__ == '__main__':
# 创建线程
# group = None, 使用默认
# target = None, 指定函数方法
# name = None, 指定线程名称
# args = (), kwargs = None, 传递参数
# daemon = None 设置守护线程
t1 = threading.Thread(target=func,args=('python',),name='t1')
t2 = threading.Thread(target=func,kwargs={'data':'itcast'},name='t2')
# 启动运行线程
t1.start()
t2.start()
3.线程的注意点
-
线程之间执行是无序的
-
线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。
-
进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行。
-
-
主线程会等待所有的子线程执行结束再结束
-
如何让主进程退出子进程销毁,不让主进程再等待子进程去执行。——设置守护主线程
-
第一种设置守护线程:daemon=True
-
第二种:线程对象.setDaemon(True)
# 1.创建线程时传递守护进程参数 daemon=True t1 = threading.Thread(target=func,name='t1',daemon=True) # 2.线程对象调用 setDaemon(True) t1.setDaemon(True)
-
import threading # 主线程会等待子线程结束后在结束 def func(): print('线程关系') print('子线程:',threading.current_thread().name) if __name__ =="__main__": # 创建线程 t1 = threading.Thread(target=func,name='t1',daemon=True) t2 = threading.Thread(target=func,name='t2') # 调用 t1.start() t2.start() # 默认情况下主线程会等待所有子线程结束后才真正结束主线程程序 # 可以通指定守护线程的形式,当主线程结束后自动结束子线程 t2.setDaemon(True) print('主线程')
-
-
线程之间共享全局变量
import threading data_list = [] def write_data(): # 往全局列表中写入数据 for i in range(0, 50): data_list.append(i) print('write_data方法写入的数据为:', data_list) def read_data(): print('read_data方法读取的数据为:', data_list) if __name__ =='__main__': # 创建多线程程分别调用两个读写方法,验证全局变量是否共享数据 t1 = threading.Thread(target=write_data) t2 = threading.Thread(target=read_data) t1.start() t2.start() print('主线程读取的数据:',data_list)
write_data方法写入的数据为: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49] read_data方法读取的数据为: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49] 主线程读取的数据: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
通过上述代码展示:线程 t1 与线程 t2 分别执行对全局列表 data_list 的写入与读取操作·,不难发现,t2读取的数据与总线程读取的数据均为t1写入的数据。所以:线程之间共享全局变量
-
线程之间共享全局变量数据出现错误问题(线程的资源抢占)
-
在执行多线程代码时,有时会发现全局变量数据不一样? 错误分析: 例如 两个线程first_thread和second_thread都要对全局变量g_num(默认是0)进行加1运算,但是由于是多线程同时操作,有可能出现下面情况:在g_num=0时,first_thread取得g_num=0。此时系统把first_thread调度为”sleeping”状态,把second_thread转换为”running”状态,t2也获得g_num=0 然后second_thread对得到的值进行加1并赋给g_num,使得g_num=1。然后系统又把second_thread调度为”sleeping”,把first_thread转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。 这样导致虽然first_thread和first_thread都对g_num加1,但结果仍然是g_num=1
-
全局变量数据错误的解决办法:利用线程同步: (.join())
-
保证同一时刻只能有一个线程去操作全局变量 同步: 就是协同步调,按预定的先后次序进行运行。
-
t1.start() t1.join() t2.start() t2.join()
案例展示
-
-
import threading
data = 0
def func1():
global data
for i in range(0,1000000):
data+=1
print(f'函数1的累加结果:{data}')
def func2():
global data
for i in range(0, 1000000):
data += 1
print(f'函数2的累加结果:{data}')
if __name__ == '__main__':
# 创建两个线程进程累加
t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)
# 通过join的形式让函数顺序执行
t1.start()
# t1.join()
t2.start()
# t2.join()
print(f'主线程的结果展示:{data}')
主线程的结果展示:506845
函数1的累加结果:1383100
函数2的累加结果:1285542
4. 进程与线程对比
-
进程和线程都是完成多任务的一种方式
-
多进程要比多线程消耗的资源多,但是多进程开发比单进程多线程开发稳定性要强,某个进程挂掉不会影响其它进程。
-
多进程可以使用cpu的多核运行,多线程可以共享全局变量。
-
线程不能单独执行必须依附在进程里面