文章目录
- 多任务的介绍
- 多任务的执行方式
- 进程
- 进程的概念
- 进程的作用
- 进程的使用
- 多进程
- 获取进程编号
- 进程执行带有参数的任务
- 介绍
- args参数和kwargs的使用
- 进程注意点
- 进程之间不共享全局变量
- 主进程会等待所有子进程执行结束再结束
- 线程
- 介绍
- 概念
- 线程的作用
- 多线程的使用
- 线程执行有参数的任务
- 线程注意点
- 线程之间执行是无序的
- 主线程会等待所有的子线程执行结束再
- 线程之间共享全局变量
- 线程之间共享变量数据出现错误问题
- 互斥锁
- 互斥锁的使用
- 死锁
- 进程和线程的对比
- 关系对比
- 区别对比
- 优缺点
好久没写了,回归一下,不然被列为失踪人口了!!!👌
多任务的介绍
同一时间执行多个任务
多任务的执行方式
- 并发
在一段时间内交替去执行任务。 - 并行
对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。
进程
在Python程序中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式。
进程的概念
一个正在运行 的程序或软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。
- 注意:
- 应该程序至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程
进程的作用
单进程:
默认程序运行创建一个进程:
单进程.py --> 进程{主线程[执行代码]}
多进程:
进程的使用
多进程
# 调用包
import multiprocessing
import time
# 跳舞任务
def dance():
for i in range(3):
print('跳舞中……')
time.sleep(2)
# 唱歌任务
def sing():
for i in range(3):
print('唱歌中……')
time.sleep(1)
# 创建子进程
dance_processing = multiprocessing.Process(target=dance)
sing_processing = multiprocessing.Process(target=sing)
# 启动对应的任务
if __name__ == '__main__':
dance_processing.start()
sing_processing.start()
获取进程编号
# 调用包
import multiprocessing
import time
import os
# 跳舞任务
def dance():
# 获取当前进程(子进程)的编号
dance_os = os.getpid()
print('跳舞进程:', dance_os, multiprocessing.current_process())
# 获取当前进程的父进程编号
dance_os_parent = os.getppid()
print('跳舞的父进程id:', dance_os_parent)
for i in range(3):
print(f'跳{i+1}舞中……')
time.sleep(2)
# 唱歌任务
def sing():
# 获取当前进程(子进程)的编号
sing_os = os.getpid()
print('唱歌进程:', sing_os, multiprocessing.current_process())
# 获取当前进程的父进程编号
sing_os_parent = os.getppid()
print('唱歌的父进程id:', sing_os_parent)
for i in range(3):
print(f'唱{i+1}歌中……')
time.sleep(1)
os.kill(sing_os, 9)
if __name__ == '__main__':
# 获取主进程id
main_os = os.getpid()
print('主进程:', main_os, multiprocessing.current_process())
# 创建子进程
dance_processing = multiprocessing.Process(target=dance, name='dance')
# print('跳舞进程:', dance_processing)
sing_processing = multiprocessing.Process(target=sing, name='sing')
# print('唱歌进程:', sing_processing)
# 启动对应的任务
dance_processing.start()
sing_processing.start()
进程执行带有参数的任务
介绍
Process类执行任务并给任务传参有两种方式:
- args 表示以元祖的方式给执行任务传参
- kwargs 表示以字典方式给执行任务传参
args参数和kwargs的使用
import multiprocessing
import time
# 跳舞任务
def dance(count):
for i in range(count):
print('跳舞中……')
time.sleep(2)
# 唱歌任务
def sing(count):
for i in range(count):
print('唱歌中……')
time.sleep(1)
# 启动对应的任务
if __name__ == '__main__':
# 创建子进程
dance_processing = multiprocessing.Process(target=dance, args=(3,))
sing_processing = multiprocessing.Process(target=sing, kwargs={'count': 2})
dance_processing.start()
sing_processing.start()
进程注意点
- 进程之间不共享全局变量
- 主进程会等待所有的子进程结束再结束
进程之间不共享全局变量
import multiprocessing
import time
# 定义全局变量列表
g_list = []
# 添加数据的任务
def add_data():
for i in range(3):
# 不用加global(因为列表是可变类型,可以在原有内存的基础上修改数据,并且修改后内存地址不变)
g_list.append(i)
# 加上global 表示声明要修改全局变量的内存地址
# g_list = [1, 2]
print('append', i)
time.sleep(0.2)
print('添加完成:', g_list)
# 读取数据的任务
def read_data():
print('read:', g_list)
if __name__ == '__main__':
# 添加数据的子进程
add_process = multiprocessing.Process(target=add_data)
# 读取数据的子进程
read_process = multiprocessing.Process(target=read_data)
# 启动进程
add_process.start()
# 当前进程执行完成再执行完再继续往下执行
add_process.join()
read_process.start()
- 结论: 进程之间不共享变量
主进程会等待所有子进程执行结束再结束
根据以上代码测试,相信你已经发现了
线程
介绍
在Python中,想要实现多任务除了使用进程以外,还可以使用线程来完成,线程是实现多任务的另一种方式。
概念
线程是进程中执行代码的一个分支,每一个执行分支(线程)想要执行代码需要cpu进行调度,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。
线程的作用
多线程可以完成多任务
多线程:
程序启动默认会有一个主线程,程序员自己创建的线程可以称为子线程,多线程可以完成多任务
多线程的使用
# 导入线程模块
import threading
import time
def dance():
# 获取当前线程
dance_thread = threading.current_thread()
print('跳舞:', dance_thread)
for i in range(3):
print('跳舞ing')
time.sleep(2)
def sing():
# 获取当前线程
current_thread = threading.current_thread()
print('唱歌:', current_thread)
for i in range(3):
print('唱歌ing')
time.sleep(1)
if __name__ == '__main__':
# 获取当前线程
main_thread = threading.current_thread()
print('主线程:', main_thread)
# 创建子线程
dance_thread = threading.Thread(target=dance, name='dance')
sing_thread = threading.Thread(target=sing, name='sing')
# 启动子程序执行对于的任务
dance_thread.start()
sing_thread.start()
结果:
主线程: <_MainThread(MainThread, started 11968)>
跳舞: <Thread(dance, started 10584)>
跳舞ing
唱歌: <Thread(sing, started 3440)>
唱歌ing
唱歌ing
跳舞ing
唱歌ing
跳舞ing
线程执行有参数的任务
Thread类执行任务并给任务传参数有两种
- args 表示以元祖的方式给执行任务传参
- kwargs 表示以字典方式给执行任务传参
# 导入线程模块
import threading
import time
def dance(count):
# 获取当前线程
dance_thread = threading.current_thread()
print('跳舞:', dance_thread)
for i in range(count):
print('跳舞ing')
time.sleep(2)
def sing(count):
# 获取当前线程
current_thread = threading.current_thread()
print('唱歌:', current_thread)
for i in range(count):
print('唱歌ing')
time.sleep(1)
if __name__ == '__main__':
# 获取当前线程
main_thread = threading.current_thread()
print('主线程:', main_thread)
# 创建子线程
dance_thread = threading.Thread(target=dance, name='dance', args=(2,))
sing_thread = threading.Thread(target=sing, name='sing', kwargs={'count': 3})
# 启动子程序执行对于的任务
dance_thread.start()
sing_thread.start()
结果:
主线程: <_MainThread(MainThread, started 7412)>
跳舞: <Thread(dance, started 15816)>
跳舞ing
唱歌: <Thread(sing, started 18212)>
唱歌ing
唱歌ing
跳舞ing
唱歌ing
线程注意点
- 线程之间是无序的
- 主线程会等待所有的子线程结束再结束
- 线程之间共享全局变量
- 线程之间共享全局变量数据出现错误问题
线程之间执行是无序的
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()
结果:
<Thread(Thread-4 (task), started 16424)><Thread(Thread-11 (task), started 16816)><Thread(Thread-5 (task), started 11396)>
<Thread(Thread-17 (task), started 14800)>
<Thread(Thread-13 (task), started 23500)><Thread(Thread-20 (task), started 28080)>
<Thread(Thread-9 (task), started 32000)>
<Thread(Thread-6 (task), started 15764)><Thread(Thread-19 (task), started 5112)>
<Thread(Thread-1 (task), started 33744)>
<Thread(Thread-2 (task), started 26248)>
<Thread(Thread-18 (task), started 19632)><Thread(Thread-15 (task), started 20116)>
<Thread(Thread-14 (task), started 24540)><Thread(Thread-10 (task), started 28732)>
<Thread(Thread-12 (task), started 10912)>
<Thread(Thread-16 (task), started 25176)><Thread(Thread-8 (task), started 14792)>
<Thread(Thread-7 (task), started 20020)>
<Thread(Thread-3 (task), started 28072)>
线程之间执行是无序的,具体那个线程执行是由cpu调度决定的
主线程会等待所有的子线程执行结束再
import threading
import time
def task():
while True:
print('任务执行中……')
time.sleep(0.3)
if __name__ == '__main__':
# 创建子进程
task_thread = threading.Thread(target=task)
# 启动子线程对应的任务
task_thread.start()
# 主进程延迟1s
time.sleep(1)
print('over')
结果:
任务执行中……
任务执行中……
任务执行中……
任务执行中……
over
任务执行中……
任务执行中……
任务执行中……
任务执行中……
......
线程之间共享全局变量
import threading
# 定义全局变量
g_list = []
# 添加数据的任务
def add_data():
for i in range(1, 4):
g_list.append(i)
print('add:', i)
print('添加完成')
# 创建读取的任务
def read_data():
print('g_list:', g_list)
if __name__ == '__main__':
add_thread = threading.Thread(target=add_data)
read_thread = threading.Thread(target=read_data)
add_thread.start()
add_thread.join()
read_thread.start()
结果:
add: 1
add: 2
add: 3
添加完成
g_list: [1, 2, 3]
线程之间共享变量数据出现错误问题
import threading
g_num = 0
def task1():
for i in range(10000000):
global g_num
g_num += 1
print('task1', g_num)
def task2():
for i in range(10000000):
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()
总数输出可能比200w少
解决方法:
线程同步:保证同一时刻只能有一个线程去操作全局变量,同步就是协同步调,按预定的先后次序进行运行。
线程同步的方式:
- 线程等待(join)
- 互斥锁
互斥锁
对共享数据进行锁定,保证同一时刻只能有一个线程去操作
注意:
互斥锁是多个线程一起去抢,抢到锁的线程先执行,没抢到的线程需要等待,等互斥锁使用完释放后,其他等待的线程再去抢这个锁。
互斥锁的使用
import threading
g_num = 0
# 创建互斥锁,Lock本质是一个函数,通过调用函数可以创建一个互斥锁
lock = threading.Lock()
def task1():
# 上锁
lock.acquire()
for i in range(10000000):
global g_num
g_num += 1
print('task1', g_num)
# 释放锁
lock.release()
def task2():
# 上锁
lock.acquire()
for i in range(10000000):
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()
结果
task1 10000000
task2 20000000
总结:线程等待和互斥锁都是把多任务改成单任务去执行,保证了数据的准确性,但是执行性能会下降
死锁
一直等待对方释放锁的情景就是死锁
进程和线程的对比
- 关系对比
- 区别对比
- 优缺点对比
关系对比
- 线程是依附在进程里面的,没有进程就没有线程
- 一个进程默认提供一条线程,进程可以创建多个线程
区别对比
- 进程之间不共享全局变量
- 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法:互斥锁或者线程同步
- 创建进程的资源开销要比创建线程资源开销大
- 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
- 线程不能够独立执行,必须依存在进程中
- 多进程开发比单进程/多线程开发稳定性要强
优缺点
- 进程:
- 优点:可以用多核
- 缺点:资源开销大
用于计算密集型的相关操作使用多进程
- 线程:
- 优点:资源开销小
- 缺点:不能使用多核
文件写入,文件的下载,I/O操作
'有爬虫需求的dd我哦'
lu_txd