一 线程

1创建线程

from threading import Thread#导入线程模块
import time#导入时间模块

li = []
#写入数据
def wdata():
    for i in range(5):
        li.append(i)
        time.sleep(1)
    print("写入的数据是",li)

#读取数据
def rdata():
    print("读取的数据是",li)

if __name__ == '__main__':
    #创建子线程
    t1 = Thread(target=wdata)
    t2 = Thread(target=rdata)
    #开启子线程
    t1.start()
    t2.start()

2阻塞线程

if __name__ == '__main__':
    #创建子线程
    t1 = Thread(target=fun1)
    t2 = Thread(target=fun2)
    #开启子线程
    t1.start()
    t1.join()#阻塞主线程,直到t1完成
    t2.start()
    t2.join()#阻塞主线程,直到t2完成

3互斥锁

当一个锁执行时,另一个线程也要使用同一个锁,将会等待锁执行完成
加锁后必须解锁

lock = Lock()#声明锁 必须是公共变量

lock.acquire()#加锁
#代码块
lock.release()#解锁
from threading import Thread,Lock#导入锁
import time

a = 0
b = 2000000
lock = Lock()#声明锁
#写入数据
def funa():
    lock.acquire()#加锁
    for i in range(b):
        global a
        a+=1
    print("方法1a的值",a)
    lock.release()#解锁
#读取数据
def funb():
    lock.acquire()
    for i in range(b):
        global a
        a += 1
    print("方法2a的值", a)
    lock.release()
if __name__ == '__main__':
    #创建子线程
    t1 = Thread(target=funa)
    t2 = Thread(target=funb)
    #开启子线程
    t1.start()
    t2.start()

二 线程

1常用成员

py提供了Process类代表进程对象
Process类构造参数
target:执行的任务名称,即子进程要执行的任务
args 用元组形式传参
kwargs 用字典形式传参

方法
start() 开启子进程
isAlive() 子进程是否还活着
join() 主进程等待子进程执行结束,阻塞

属性
name 进程的名字 默认Process-N N是从递增的数字
pid 进程号

2启动一个进程

from multiprocessing import Process#导入模块
def sing():
    print('sing')
def dance():
    print('dance')

if __name__ == '__main__':
    #创建子进程
    p1 = Process(target=sing)#定义进程
    p2 = Process(target=dance)
    p1.start()#开启进程   打印sing
    p2.start()#dance    打印dance
    print("p1",p1.name)#p1 Process-1  比sing和dance的打印先执行
    print("p2",p2.name)#p2 Process-2  比sing和dance的打印先执行

3 进程的名字

默认名字是 Process-N

    p1 = Process(target=sing)#分配默认名字
    p1.start()#开启进程   打印sing
    print("p1",p1.name)#p1 Process-1  比sing的打印先执行

定义自定义名字

    p1 = Process(target=sing,name="我喜欢唱歌")#创建时自定义名字
    p1.start()#开启进程   打印sing
    print("p1",p1.name)#p1 我喜欢唱歌  比sing的打印先执行
	 #也可以用对象改
	 p1.name="我的名字"

4 进程的编号

获取子进程编号

    p1 = Process(target=sing)
    p1.start()
    print("p1",p1.pid)#p1 44956

获取当前进程和父进程的编号

from multiprocessing import Process
import os#导入os模块来获取当前的进程号

def sing():
    print('sing的pid',os.getpid())
    print("sing的父进程pid",os.getppid())

if __name__ == '__main__':
    p1 = Process(target=sing)
    p1.start()
print("python程序的Pid",os.getpid())

输出结果
在这里插入图片描述

5 启动进程时传参

from multiprocessing import Process
def eat(name):
    print('eat', name)
if __name__ == '__main__':
    p = Process(target=eat, args=("xiaoming",))#注意单参数元组必须加逗号
    p.start()#eat xiaoming

6 子进程存活监测与阻塞

from multiprocessing import Process
def eat(name):
    print('eat', name)
if __name__ == '__main__':
    p = Process(target=eat, args=("xiaoming",))
    p.start()
    print(p.is_alive())  #永远是存活,因为这里先执行,子进程会稍后执行
from multiprocessing import Process
def eat(name):
    print('eat', name)
if __name__ == '__main__':
    p = Process(target=eat, args=("xiaoming",))
    p.start()
    p.join()#使用join,主进程会等子进程执行完毕再执行
    print(p.is_alive())#False 结果永远是false

7 进程之间的通信

和线程不同,进程之间无法共享全局变量

7.1队列

常用方法
put()添加 先入先出,如果已经满了还加,程序会阻塞永远卡死
get()取出
empty()是否为空
full()是否已经满了
size()当前还有多少数据没取

from queue import Queue#引入模块

q = Queue(3)#指定队列长度,空或者负值代表没有上限
q.put(1)#加入一条数据
q.put("第二条数据")
q.put("第三条数据")
print(q.get())#1   取出消息后,第一条会移除
print(q.get())#第二条数据
print(q.empty())#False
7.2 进程使用队列跨进程通信

必须使用进程模块提供的Queue类才能跨进程通信,普通的Queue类无法跨进程

from multiprocessing import Process,Queue

正常情况下进程之间不会共享任何数据

from multiprocessing import Process,Queue
li = []
def wData():
    global li
    for i in range(5):
        li.append(i)
    print("写入",li)
def rData():
    global li
    print("读取",li)

if __name__ == '__main__':
    p1 = Process(target=wData)
    p2 = Process(target=rData)
    p1.start()#写入 [0, 1, 2, 3, 4]
    p2.start() #读取 []

使用Queue可以跨进程传递数据

from multiprocessing import Process,Queue
def wData(q):
    for i in range(5):
        q.put(i)
        print("写入",i)
def rData(q):
    while True:
        if q.empty():
            break
        else:
            print("读取",q.get())
if __name__ == '__main__':
    q = Queue()#注意队列必须声明在这个if下,不能放在最外层
    p1 = Process(target=wData, args=(q,))
    p2 = Process(target=rData, args=(q,))
    p1.start()#写入0 写入1 写入2 写入3 写入4
    p1.join()
    p2.start() #读取0 读取1 读取2 读取3 读取4

三 协程

1 定义

生成器就是协程

def myCoroutine():#一个普通方法,带yield自动识别为协程
    yield "a"
    yield "b"
    yield "c"

g = myCoroutine()#协程对象
print(next(g))#a  使用next执行下一个yield
print(next(g))#b
print(next(g))#c

常用于IO操作 如文件读写 网络请求

2 Greenlet

第三方模块,封装了协程,可以手动切换正在执行的任务

2.1安装

注意如果使用pycharm编辑,会自动给项目创建一个虚拟环境,如果使用windows的cmd会导致安装环境不一致,所以要用pycharm的控制台
在这里插入图片描述

安装命令:

pip install greenlet   或 python -m pip install greenlet

卸载

pip uninstall greenlet 

查看安装路径

pip show greenlet
2.2 使用

基础

from greenlet import greenlet#引入模块

#这是两个普通的方法,并非生成器
def sing():
    print("在唱歌")
    print("唱完了")

def dance():
    print("在跳舞")
    print("跳完了")

if __name__ == "__main__":
    g1 = greenlet(sing)
    g2 = greenlet(dance)
    g1.switch()#在唱歌 唱完了  只有见到switch才会执行
    g2.switch()#在跳舞 跳完了

任务间切换

from greenlet import greenlet#引入模块

#这是两个普通的方法,并非生成器
def sing():
    print("在唱歌")
    g2.switch()  #在这里将任务切换到别的方法
    print("唱完了")

def dance():
    print("在跳舞")
    print("跳完了")

if __name__ == "__main__":
    g1 = greenlet(sing)
    g2 = greenlet(dance)
    g1.switch()#在唱歌 在跳舞 跳完了  执行sing的方法,遇到g2.switch()切换到dance,执行完dance
    g1.switch()#唱完了 #然后继续执行剩下的sing

3 Gevent

和greenlet类似,但是他遇到IO操作时,会自动切换程序
gevent基于greenlet

3.1安装与卸载

注意如果使用pycharm编辑,会自动给项目创建一个虚拟环境,如果使用windows的cmd会导致安装环境不一致,所以要用pycharm的控制台
在这里插入图片描述
安装命令:

python -m pip install gevent
3.2 常用函数
gevent.spawn(函数名) 创建协程对象
gevent.sleep()       协程等待,类似time.sleep,gevent中必须用geven.sleep
gevent.join()			  阻塞,等待某个协程执行结束
gevent.joinall()     等待所有协程对象都执行结束再退出,参数是一个协程对象列表

使用实例1 这里没有任何耗时操作,只是为了辅助理解调度池

import gevent

def sing():
    print("在唱歌")
    print("唱完了")

def dance():
    print("在跳舞")
    print("跳完了")

if __name__ == '__main__':
    #创建协程对象  将协程对象自动放入调度池,但不会执行
    g1 = gevent.spawn(sing)
    g2 = gevent.spawn(dance)
    g2.join()#等待g2执行结束  join的作用是阻塞主线程并等待协程执行完毕  协程会按加入调度池的顺序依次执行 打印结果为 在唱歌 唱完了 在跳舞 跳完了

使用实例2

import gevent

def sing():
    print("在唱歌")
    gevent.sleep(1)#注意不能用time.sleep
    print("唱完了")

def dance():
    print("在跳舞")
    gevent.sleep(3)
    print("跳完了")

if __name__ == '__main__':
    #创建协程对象  将协程对象自动放入调度池,但不会执行
    g1 = gevent.spawn(sing)
    g2 = gevent.spawn(dance)
    g2.join()#打印结果 在唱歌 在跳舞 唱完了  没有跳完了 因为程序先执行g1,遇到sleep切换到g2,g1g2的睡眠同步计时,但是g1只睡1秒,g2来不及执行,此时程序已经退出  如果想让g2完整执行,需要加上g2.join()

使用实例 3 joinall的使用,等所有协程执行完

import gevent

def sing():
    print("在唱歌")
    gevent.sleep(1)#注意不能用time.sleep
    print("唱完了")

def dance():
    print("在跳舞")
    gevent.sleep(3)
    print("跳完了")

if __name__ == '__main__':
    #创建协程对象  将协程对象自动放入调度池,但不会执行
    g1 = gevent.spawn(sing)
    g2 = gevent.spawn(dance)
    gevent.joinall([g1, g2])#打印结果 在唱歌 在跳舞 (一秒后)唱完了  (2秒后)跳完了

使用实例 4 传参

def sing(name):
    print(f"{name}在唱歌")
    gevent.sleep(1)#注意不能用time.sleep
    print("唱完了")

g1 = gevent.spawn(sing,"我的名字")
3.3 Monkey 补丁

在模块运行时可以将原本的time.sleep 替换为gevent.sleep

from gevent import mokey

monkey.patch_all()#必须放在前边 ,运行时会自动把time.sleep替换为gevent.sleep
def sing(name):
    print(f"{name}在唱歌")
    time.sleep(1)
    print("唱完了")

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐