python 多进程多线程 学习笔记

python的学习笔记。不能坐以待毙,我还什么都不会呢!


创建进程

Multiprocessing模块中创建进程的类主要有两个:Process类(进程类)和Pool类(进程池类)

使用Process类创建进程

start是创建并启动子进程的函数

join是等待子进程结束的函数

如果不写join,可能导致"主进程结束"在"子进程标识"前输出了。

sp1.join()检查sp1是否还在运行,如果还在运行,主进程进入等待状态。操作系统需要把CPU让给sp1,等他结束了主进程再结束。

python 复制代码
from multiprocessing import Process
import os
def spfunc(spname):
    print("子进程标识:%s,ID:%s"%(spname,os.getpid()))
if __name__ == "__main__":
    print("主进程开始:")
    print("主进程ID:%s"%os.getpid())
    sp1 = Process(target=spfunc,args=('sp1',))
    sp2 = Process(target=spfunc,args=('sp2',))
    sp1.start()
    sp2.start()
    sp1.join()
    sp2.join()
    print("主进程结束。")

使用Process类的派生类创建进程:

python 复制代码
import os,time
from multiprocessing import Process
class SubProcess(Process):
    def __init__(self):
        Process.__init__(self)
    def run(self):
        print("子进程名称 = {0},ID = {1}".format(self.name,self.pid))
        time.sleep(0.1)
if __name__ == '__main__':
    print("主进程开始:")
    for i in range (3):
        sp = SubProcess()
        sp.start()
        sp.join()
    print("主进程结束。")

使用Pool类创建进程

如果要启动大量子进程,可以用Pool类的方式批量创建子进程。

python 复制代码
import multiprocessing,time,os
def spfunc(i):
    print("子进程ID = {},结果 = {}".format(os.getpid(),i**2))
    time.sleep(1)
if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    for i in range (1,4):
        pool.apply(spfunc, (i,))
    pool.close()
    pool.join()

进程通信

使用Queue实现进程间通信

python 复制代码
from multiprocessing import Process,Queue
import os,time,random
def writeQueue(q):
    for i in range(5):
        n = random.randint(1,100)
        print("放进队列的随机数为:%d"%n)
        q.put(n)
        time.sleep(1)
def readQueue(q):
    for i in range(5):
        d = q.get(True)
        print("从队列中读取的数据为:%d"%d)
if __name__ == '__main__':
    q = Queue()
    spw = Process(target=writeQueue,args=(q,))
    spr = Process(target=readQueue,args=(q,))
    spw.start()
    spr.start()
    spw.join()
    spr.join()

使用Pipes完成进程间通信

python 复制代码
from multiprocessing import Pipe,Process
def spfunc(cPipe):
    while(True):
        try:
            print("接受的信息:",cPipe.recv())
        except EOFError:
            break
if __name__ == '__main__':
    parentPipe,childPipe = Pipe(True)
    sp = Process(target=spfunc,args=(childPipe,))
    sp.start()
    parentPipe.send("量子通信")
    parentPipe.send("引力波")
    parentPipe.close()
    sp.join()

使用Manager对象实现进程间通信

python 复制代码
from multiprocessing import Process,Manager
def spfunc(dict,list):
    dict['Alice'] = 21
    dict['Beth'] = 22
    list.append("physics")
    list.append("chemistry")
if __name__ == '__main__':
    manager = Manager()
    list = manager.list()
    dict = manager.dict()
    sp = Process(target=spfunc,args=(dict,list))
    sp.start()
    sp.join()
    print("list:",list)
    print("dict:",dict)

使用数组实现进程间数据交换

在这里学到了一个概念,最关键的一句是这个:

num = multiprocessing.Array("i", [1,3,5])

我大可以直接写num = [1,3,5],但是用这里的数组生成,它的最大区别就是:

这是共享数组,主进程和子进程都可以访问并修改它,然后修改记录同步到两个进程中。

python 复制代码
import multiprocessing
def spfunc(num):
    for i in range (3):
        num[i] **= 3
if __name__ == '__main__':
    num = multiprocessing.Array("i", [1,3,5])
    print("初始数组:",num[:])
    sp = multiprocessing.Process(target=spfunc,args=(num,))
    sp.start()
    sp.join()
    print("修改后的数组:",num[:])

进程同步

锁实现进程同步

上锁:一个进程执行完再执行另一个

sp1:number=1

sp1:number=4

sp1:number=9

sp2:number=1

sp2:number=8

sp2:number=27

把锁注释掉:两个进程交替打印,互相干扰

sp1:number=1

sp2:number=1

sp1:number=4

sp2:number=8

sp1:number=9

sp2:number=27

python 复制代码
from multiprocessing import Process,Lock
import time,os
def spfunc(spname,v,lock):
    #lock.acquire()
    for i in range(1,4):
        n = i**v
        time.sleep(0.5)
        print("{0}:number={1}".format(spname,n))
    #lock.release()
if __name__ == "__main__":
    lock = Lock()
    sp1 = Process(target=spfunc,args=("sp1",2,lock))
    sp2 = Process(target=spfunc,args=("sp2",3,lock))
    sp1.start()
    sp2.start()

Event实现进程同步

set:V操作,wait:P操作,clear:充值操作

准备买商品...

下订单、付款...

接收订单和贷款...

发货...

收到商品...

评价商品...

欢迎再次订购

python 复制代码
import multiprocessing,time
def sjfunc(event):
    print("接收订单和贷款...")
    print("发货...")
    event.set()
    event.clear()
    event.wait()
    print("欢迎再次订购")
def gkfunc(event):
    print("准备买商品...")
    print("下订单、付款...")
    event.wait()
    print("收到商品...")
    print("评价商品...")
    event.set()
    event.clear()
if __name__ == '__main__':
    event = multiprocessing.Event()
    sjp = multiprocessing.Process(target=sjfunc,args=(event,))
    gkp = multiprocessing.Process(target=gkfunc,args=(event,))
    gkp.start()
    time.sleep(0.5)
    sjp.start()

线程创建

threading类

python 复制代码
from threading import Thread
import time
def thfunc(i):
    print("线程%d启动"%i)
    time.sleep(1)
if __name__ == '__main__':
    print("主线程开始...")
    for i in range (3):
        th = Thread(target=thfunc,args=(i,))
        th.start()
        th.join()
    print("主线程结束。")

threading派生类

python 复制代码
import time,threading
class mythread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        for i in range (3):
            print("这里是线程%s中的%d"%(self.getName(),i))
            time.sleep(1)
if __name__ == '__main__':
    th1 = mythread()
    th2 = mythread()
    th1.start()
    th2.start()
    th1.join()
    th2.join()

线程池创建线程

threadpool模块:

线程池有3个线程,刚好有3个任务

线程1:执行 addfunc(1, 2) → 输出 "1+2=3"

线程2:执行 addfunc(3, 4) → 输出 "3+4=7"

线程3:执行 addfunc(5, 6) → 输出 "5+6=11"

由于有3个线程,3个任务同时执行

输出顺序可能不同,取决于哪个线程先完成

python 复制代码
import threadpool
def addfunc(op1,op2):
    print("%d+%d=%d"%(op1,op2,op1+op2))
if __name__ == '__main__':
    oplist1 = [1,2]
    oplist2 = [3,4]
    oplist3 = [5,6]
    oplist = [(oplist1,None),(oplist2,None),(oplist3,None)]
    pool = threadpool.ThreadPool(3)
    requests = threadpool.makeRequests(addfunc,oplist)
    #[pool.putRequest(req) for req in requests]
    for req in requests:
        pool.putRequest(req)
    pool.wait()

concurrent.futures模块创建线程池

map和submit都是输出函数。

python 复制代码
from concurrent.futures import ThreadPoolExecutor
def executetask(task):
    print("执行:%s"%(task))
if __name__ == '__main__':
    tasklist = []
    for i in range(5):
        tasklist.append("任务"+str(i))
    with ThreadPoolExecutor(5) as ect:
        #for task in tasklist:
            #future = ect.submit(executetask,task)
        future = ect.map(executetask,tasklist)

线程通信

python 复制代码
import threading,queue,time
class mythread(threading.Thread):
    def __init__(self,q,i):
        super(mythread,self).__init__()
        self.q = q
        self.i = i
    def run(self):
        self.q.put("这是第%d个线程..."%(self.i))
if __name__ == '__main__':
    q = queue.Queue()
    for i in range(3):
        mythread(q,i).start()
    while not q.empty():
        print(q.get())

线程同步

lock实现同步

加了锁都是线程A,不加锁如下

当前线程是A,num=1

当前线程是B,num=2

当前线程是A,num=3

python 复制代码
import threading,time
def countfunc(tname,lock):
    global num;
    #lock.acquire()
    while True:
        if num <= 3:
            print("当前线程是%s,num=%s"%(tname,num))
            num += 1
            time.sleep(1)
        else:
            break
    #lock.release()
if __name__ == '__main__':
    num = 1
    lock = threading.Lock()
    t1 = threading.Thread(target=countfunc,args=('A',lock))
    t2 = threading.Thread(target=countfunc,args=('B',lock))
    t1.start()
    t2.start()

Condition实现同步

  • 原版:两个线程都是"先等待,后通知",导致第一次打印后就互相等待

  • 修改版:奇数线程"先通知,后等待",确保了总有线程能唤醒对方

python 复制代码
import threading,time
def thfunc1(thname,cond):
    with cond:
        for i in range(0,6,2):
            print("线程%s:%d."%(thname,i))
            cond.wait()
            cond.notify()
def thfunc2(thname,cond):
    with cond:
        for i in range(1,6,2):
            print("线程%s:%d."%(thname,i))
            cond.notify()
            cond.wait()
if __name__ == '__main__':
    cond = threading.Condition()
    th1 = threading.Thread(target=thfunc1,args=("th1",cond))
    th2 = threading.Thread(target=thfunc2,args=("th2",cond))
    th1.start()
    th2.start()
    th1.join()
    th2.join()

Event对象实现同步

event.isSet() or event.set()是判断是否为false,如果是false那么改成true,也就是有事件发生。

event.wait() # 等待第一个事件(加班通知),等待第二个事件(吃夜宵通知)。

python 复制代码
import time,threading
def bossfunc(thname):
    print("%s:今晚加班到11点"%thname)
    event.isSet() or event.set()
    time.sleep(2)
    print("%s:大家一起吃夜宵"%thname)
    event.isSet() or event.set()
def employeefunc(thname,i):
    event.wait()
    print("%s %d:好痛苦..."%(thname,i))
    time.sleep(1)
    event.clear()
    event.wait()
    print("%s %d;太好了"%(thname,i))
if __name__ == '__main__':
    event = threading.Event()
    thboss = threading.Thread(target=bossfunc,args=("thBoss",)).start()
    for i in range(4):
        themployee = threading.Thread(target=employeefunc,args=("thEmployee",i)).start()

案例分析

使用多进程导入/导出数据

使用多进程将多个excel文件的书籍信息导入SQLite3数据库

python 复制代码
import multiprocessing, sqlite3, os, openpyxl
def create_connect_database():
    try:
        #拼接数据库文件路径
        conn = sqlite3.connect(os.getcwd() + '\\resources\\bookDB.db')#os.getcwd()获取当前工作目录
        cur = conn.cursor() #创建游标,执行sql语句和获取结果
        #创建数据表(如果不存在)
        cur.execute(
            """create table if not exists table_book(ID text PRIMARY KEY,Name text not null,Autore text,Price float);""")
        conn.commit()#提交事务,保存
    except Exception as e:
        print("创建/链接数据库或者创建数据表失败:", e)
def eachxlsx(xlsxfn):
    wb = openpyxl.load_workbook(xlsxfn) #加载excel文件
    ws = wb.worksheets[0]   #获取第一个工作表(就是sheet1)
    for index, row in enumerate(ws.rows):
        if index == 0:  #跳过标题行,因为是列标题
            continue
        yield tuple(map(lambda x: x.value, row))
def spfunc(filename):
    with sqlite3.connect(os.getcwd() + '\\resources\\bookDB.db') as conn:
        try:
            cur = conn.cursor()
            sql = 'INSERT INTO table_book VALUES(?,?,?,?)'
            cur.executemany(sql, eachxlsx(filename))
            conn.commit()
        except Exception as e:
            print("导入数据失败.", e)
if __name__ == '__main__':
    print("Excel文件导入数据库开始...")
    create_connect_database()
    pool = multiprocessing.Pool()
    excel_path = os.getcwd() + "\\excel_files_dir"
    excel_File_list = os.listdir(excel_path)
    for filename in excel_File_list:
        excelFile = excel_path + "\\" + filename
        pool.apply(spfunc, (excelFile,))
    pool.close()
    pool.join()
    print("Excel文件数据导入数据库结束!")

使用多线程模拟彩票发行

zjxx = self.cpfx_q.get() #取出中奖信息,并删除

cphmlist = self.cpfx_q.get() #取出可购买的彩票号码列表,并删除

这种"取出即删除"的设计是为了保证数据不会被多个线程重复处理

python 复制代码
import threading,queue,random
class mythread(threading.Thread):
    def __init__(self,cpfx_q,i,zjhm,lock):
        super(mythread,self).__init__()
        self.cpfx_q = cpfx_q
        self.i = i
        self.zjhm = zjhm
        self.lock = lock
    def run(self):
        lock.acquire()
        #1.队列中获取当前状态
        zjxx = self.cpfx_q.get()    #取出中奖信息,并删除
        cphmlist = self.cpfx_q.get()    #取出可购买的彩票号码列表,并删除
        #2.随机购买一个号码
        cphm = random.choice(cphmlist)  #模仿随机购买彩票
        cphm_num = cphmlist.index(cphm) #查找购买的彩票号码在列表中的位置
        #3.检查是否中奖,比较购买的彩票号码和中奖号码是否相同
        if cphm == self.zjhm:
            zjxx = "抽奖人"+str(self.i)+"号购买的彩票"+str(cphm)+"中奖了!恭喜您!"
        #4.从列表中移除已购买的号码
        cphmlist.pop(cphm_num)
        #5.将更新后的状态放回队列
        self.cpfx_q.put(zjxx)
        self.cpfx_q.put(cphmlist)
        lock.release()
if __name__ == '__main__':
    print("------------------------------------------------彩票发行开始------------------------------------------------")
    cpfx_q = queue.Queue()
    cphmlist = list(range(1000,10000)) #生成[1000,1001,...,9999]
    random.shuffle(cphmlist)    #随机乱序
    zjxx = "无人中奖!"
    cpfx_q.put(zjxx)
    cpfx_q.put(cphmlist)
    zjhm = random.randrange(1000,10000)
    print("本次发行彩票中奖号码:",zjhm)
    lock = threading.Lock()
    for i in range(5000):
        mythread(cpfx_q,i,zjhm,lock).start()
    print("本次彩票发行结果:",cpfx_q.get())
    print("------------------------------------------------彩票发行结束------------------------------------------------")
相关推荐
学编程的闹钟2 小时前
E语言变量声明与使用全解析
学习
runningshark2 小时前
【DFT】Write About the Photo
笔记
你怎么知道我是队长2 小时前
前端学习---VsCode相关插件安装
前端·vscode·学习
山岚的运维笔记2 小时前
SQL Server笔记 -- 第78章:MS SQL Server 基本 DDL 操作
数据库·笔记·sql·microsoft·oracle·sqlserver
Albert Edison8 小时前
【Python】学生管理系统
开发语言·数据库·python
AomanHao9 小时前
【阅读笔记】沙尘图像线性颜色校正A fusion-based enhancing approach for single sandstorm image
图像处理·笔记·isp·图像增强·沙尘图像·色偏·颜色校正
love530love10 小时前
【ComfyUI】解决 ModuleNotFoundError: No module named ‘inference_core_nodes‘ 问题
人工智能·windows·python·comfyui·inference-core
宇木灵10 小时前
C语言基础-十、文件操作
c语言·开发语言·学习
枷锁—sha11 小时前
【pwn系列】Pwndbg 汇编调试实操教程
网络·汇编·笔记·安全·网络安全