Python中的多线程和多进程编程可以更好地利用多核CPU的性能,提高程序的运行效率。本文将介绍Python中的多线程和多进程编程基础,包括线程和进程的创建、启动、终止和管理等方面,并对比两者的优缺点和适用场景
一、多线程和多进程的区别:
通俗的理解就是想象一下火车和车厢。火车表示多进程,每个车厢表示一个线程。线程和进程的最终目的都是完成把指定的人运到指定的站点即完成任务,真正完成任务的还是车厢线程。
一个线程是一辆车火车,所以进程之间信息不能共享,多进程就像是火车的不同车厢里面有不同的人,每个车厢都是独立的,有自己的空间和资源。这些人之间不能直接交流和共享东西,因为他们在不同的车厢里。如果他们想要交流或者共享东西,就需要通过一些特殊的方式,比如通过车厢之间的门窗传递纸条或者通过列车员传递信息
而线程则是享同一个空间和资源。可以相互之间交流和共享东西,比如一起玩游戏、分享食物等。这些人之间的交流和资源共享是比较方便的,因为他们在同一个车厢里。那么线程锁就像是火车上的厕所。当多个人需要使用厕所时,只能有一个人进去,其他人需要等待。这样可以避免多个人同时进入厕所造成混乱和资源冲突。
二、多进程
import multiprocessing
import os
import time
#dance函数
def dance():
for i in range(0,3):
#获取当前进程的进程ID
print(f"dance:{i} {os.getpid()}")
#dance:0 1564
#获取当前进程的父进程ID
print("dance_parent_pid",os.getppid())
#dance_parent_pid 7180
print("我在跳舞")
time.sleep(1)
#sing函数
def sing():
for i in range(0, 3):
# 获取当前进程的进程ID
print(f"sing:{i} {os.getpid()}")
#sing:0 10888
# 获取当前进程的父进程ID
print("sing_parent_pid", os.getppid())
#sing_parent_pid 7180
print("我在唱歌")
time.sleep(1)
if __name__ == '__main__':
#创建唱歌和跳舞进程
dance_process= multiprocessing.Process(target=dance,name="dance",daemon=True)
sing_process =multiprocessing.Process(target=sing,name="sing")
#运行进程
sing_process.start()
dance_process.start()
#判断进程是否还在运行
print(f"跳舞进程是否还存在{dance_process.is_alive()}")
# 跳舞进程是否还存在True
time.sleep(1)
#强制终止进程
dance_process.terminate()
#线程等待,需等待sing进程结束后才会运行
sing_process.join()
time.sleep(5)
#查看cpu线程
print("cpu multiprocessing.cpu_count() %s" %multiprocessing.cpu_count())
#cpu multiprocessing.cpu_count() 6
#获取当前进程的
print(f"multiprocessing.current_process(){multiprocessing.current_process()}")
#multiprocessing.current_process()<_MainProcess name='MainProcess' parent=None started>
# 获取主进程的进程ID
print(f"main: {os.getpid()}")
# main: 7180
三、多线程
import threading
import os
import time
# dance函数
def dance():
for i in range(0, 3):
# 获取当前线程的线程ID
print(f"dance:{i} {threading.get_ident()}")
# dance:0 140735205947648
print("我在跳舞")
time.sleep(1)
# sing函数
def sing():
for i in range(0, 3):
# 获取当前线程的线程ID
print(f"sing:{i} {threading.get_ident()}")
# sing:0 140735205947648
print("我在唱歌")
time.sleep(1)
if __name__ == '__main__':
# 创建唱歌和跳舞线程
dance_thread = threading.Thread(target=dance, name="dance", daemon=True)
sing_thread = threading.Thread(target=sing, name="sing")
# 运行线程
sing_thread.start()
dance_thread.start()
# 判断线程是否还在运行
print(f"跳舞线程是否还存在{dance_thread.is_alive()}")
# 跳舞线程是否还存在True
time.sleep(1)
# 线程等待,需等待sing线程结束后才会运行
sing_thread.join()
time.sleep(5)
# 查看CPU线程数
print("CPU线程数", os.cpu_count())
# CPU线程数 8
# 获取当前线程
print(f"threading.current_thread() {threading.current_thread()}")
# threading.current_thread() <_MainThread(MainThread, started 140735206238784)>
# 获取主线程的线程ID
print(f"main: {threading.get_ident()}")
# main: 140735206238784
线程锁
import threading
# 共享资源
counter = 0
# 创建线程锁
lock = threading.Lock()
# 线程函数
def increment():
global counter
for _ in range(100000):
# 获取锁
lock.acquire()
try:
# 访问共享资源
counter += 1
finally:
# 释放锁
lock.release()
# 创建多个线程
threads = []
for i in range(10):
t = threading.Thread(target=increment)
threads.append(t)
# 启动线程
for t in threads:
t.start()
# 等待所有线程结束
for t in threads:
t.join()
# 打印最终结果
print("Counter:", counter)
创建了一个共享资源counter,然后使用Lock类创建了一个线程锁lock。在线程函数increment中,首先获取锁,然后对共享资源进行操作,最后释放锁。这样可以确保每次只有一个线程能够访问和修改counter,避免了并发访问导致的问题。
在使用线程锁时,需要确保在访问共享资源之前获取锁,在操作完成后释放锁。这样可以保证线程安全和数据一致性。