0
点赞
收藏
分享

微信扫一扫

Python(四、进程、线程、协程)

Star英 2021-09-27 阅读 69

多线程基本案例

import time
import threading


def single():
    for i in range(5):
        print("唱歌")
        # time.sleep(1)


def dance():
    for i in range(5):
        print("跳舞")
        # time.sleep(1)


def main():
    t1 = threading.Thread(target=single)
    t2 = threading.Thread(target=dance)
    t1.start()
    time.sleep(1)
    t2.start()
    time.sleep(1)

if __name__ == "__main__":
    main()
    # 获取当前运行的线程数,包括主线程
    print(len(threading.enumerate()))  # 3

"""
并行:真的多任务
并发:假的多任务 时间片轮转,优先级调度
"""

多线程继承案例

import threading
import time


class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = self.name + "-----" + str(i)  # name属性中保存的是当前线程的名字
            print(msg)


if __name__ == '__main__':
    t = MyThread()
    # 会调用类中的run方法
    t.start()

global的使用场景

"""
在函数中对全局变量进行修改的时候,到底是否需要使用global进行说明
要看是否对全局变量的指向进行了修改
如果修改了指向,即让全局变量指向了一个新的地方,那么必须使用global
如果仅仅修改了指向空间中的数据,则不需要global
例如:数字 字符串 元组是不可变的,针对不可变的就是指向会变化,必须添加global的

但是pycharm中在新版本解释器,只要不加global就报错,所以全部加上即可
"""

num = 100
ns = [1, 2]


def t1():
    global num
    num += 100


def t2():
    ns.append(3)
    ns += [100]


t1()
t2()

多线程问题

import threading
import time

num = 1


def t1(temp):
    global num
    for i in range(temp):
        num += 1;


def t2(temp):
    global num
    for i in range(temp):
        num += 1;


def main():
    # args传递的元组对应就是函数中的temp
    t11 = threading.Thread(target=t1, args=(1000000,))
    t22 = threading.Thread(target=t2, args=(1000000,))
    t11.start()
    t22.start()
    time.sleep(3)
    print(num) #1453092


if __name__ == '__main__':
    main()

互斥锁

import threading
import time

num = 1


def t1(temp):
    global num

    for i in range(temp):
        """
        尽量减少加锁的代码,这样结果等效
        但是可以两个线程根据cpu竞争执行,不会造成一个for执行完毕全部执行完才执行下一个
        t2的for循环,效率更高
        """
        # 上锁
        mutex.acquire()
        num += 1
        mutex.release()


def t2(temp):
    global num
    for i in range(temp):
        mutex.acquire()
        num += 1
        mutex.release()


# 创建一个互斥锁
mutex = threading.Lock()


def main():
    # args传递的元组对应就是函数中的temp
    t11 = threading.Thread(target=t1, args=(1000000,))
    t22 = threading.Thread(target=t2, args=(1000000,))
    t11.start()
    t22.start()
    time.sleep(3)
    print(num)


if __name__ == '__main__':
    main()

进程

import multiprocessing

"""
进程:所有子进程会开辟单独空间,复制所有代码去单独的空间,和线程相比,更耗费资源
所以变量,属性方法等不共用

主进程不会等待子进程执行完毕才结束
"""


def t1(q):
    # 添加数据
    for i in range(5):
        q.put(i)


def t2(q):
    # 取出数据
    # waitting_list=[] 下面等效
    waitting_list = list()
    while True:
        data = q.get()
        waitting_list.append(data)
        # 队列空了就退出
        if q.empty():  # q.full()  判断队列是否满了。
            break

    print(waitting_list)



"""
进程间通信使用消息队列
"""
def main():
    # 如果添加参数,例如3这些数字,则代表最多放入3个数据,继续添加时候则需要等待,取出之后才能放进去
    q = multiprocessing.Queue()
    p1 = multiprocessing.Process(target=t1, args=(q,))
    p2 = multiprocessing.Process(target=t2, args=(q,))
    p1.start()
    p2.start()


if __name__ == '__main__':
    main()

进程池

import multiprocessing
import os


def t1(i):
    print("执行" + str(i))
    print(os.getpid())


def main():
    # 代表最大进程数3
    p = multiprocessing.Pool(3)
    for i in range(0, 10):
        # 此处添加了10个任务,但是只有3个进程,当有空闲进程之后,才会继续执行后续任务
        p.apply_async(t1, (i,))

    # 关闭进程池,关闭之后p不在接收新的请求
    p.close()
    # 等待所有子进程执行完毕,必须放在close之后,否则主进程的代码执行过快的话,可能无法打印子进程的log就主进程直接退出了
    p.join()


if __name__ == '__main__':
    main()

迭代器

from collections import Iterable
from collections import Iterator


class ClassMate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0

    def add(self, name):
        self.names.append(name)

    def __iter__(self):
        return self

    def __next__(self):
        if len(self.names) > self.current_num:
            item = self.names[self.current_num]
            self.current_num += 1
            return item
        else:
            # 这样的主动抛出异常才会在执行for  in 遍历结尾的时候自动停止
            raise StopIteration


c = ClassMate()
c.add("张三")
c.add("李四")

# 判断一个对象是否是可迭代对象用如下方法:判断依据是有没有__iter__方法
print("是否是可迭代对象:", isinstance(c, Iterable))
print(iter(c))  # <__main__.ClassMate object at 0x7f5ea1293b38>  调用iter其实就是魔法方法,获取到的返回值就是__iter__的返回值
print("是否是迭代器:", isinstance(c, Iterator))
print(next(c))  # 此时返回是空,但是通过验证可知其实就是__next()__返回值,例如给__next__直接返回20,此处打印就是20

for i in c:
    print(i)

"""
总结:
一个可迭代对象不见得是迭代器,但是迭代器肯定是可迭代对象

优势:
数据在需要的时候才生成,节省大量内存
例如:python2中range和xrange
xrange就是返回的可迭代对象
而range直接就是开启好内存的全部数据地址

但是python3中只有range,而且相当于2中的xrange

重点: 此处存储名称的案例内存优势不明显,但是如果是比如动态生成斐波那契数列的代码则优势明显
迭代器不只for循环能接收可迭代对象,list  tuple等也可以
"""

生成器(是一种特殊的迭代器)

# 创建生成器的方式一
# 和列表的区别只是一个是[]  一个是(),但是生成器更节省内存
nums = (2 * x for x in range(5))
print(nums)  # <generator object <genexpr> at 0x7f32d49c9b48>


# 创建生成器的方式二(常用)
# 函数只要有yield则是一个生成器
def create_num(index):
    a, b = 0, 1
    current_num = 0
    while current_num < index:
        yield a
        a, b = b, a + b
        current_num += 1
    return "ok"


items = create_num(10)
print(items)  # <generator object create_num at 0x7fc139e47bf8>

print(items.__next__())
print(next(items))

# 迭代方式一
for i in items:
    print(i)
# 迭代方式二(该种方式可以获取return返回值)
while True:
    try:
        print(next(items))
    except Exception as ret:
        print(ret.value)
        break

通过send方式启动生成器

def create_num(index):
    a, b = 0, 1
    current_num = 0
    while current_num < index:
        ret = yield a
        print(ret)
        a, b = b, a + b
        current_num += 1


items = create_num(10)
next(items)
# 通过send方式启动生成器,可以传入参数,此时None就是上面的ret
# 但是注意,因为是返回值,所以必须next方式先调用一次
print(items.send(None))  # 1

基于yield实现多任务

import time


def test1():
    while True:
        print("----1------")
        time.sleep(0.1)
        yield


def test2():
    while True:
        print("----2------")
        time.sleep(0.1)
        yield


def main():
    t1 = test1()
    t2 = test2()
    while True:
        next(t1)
        next(t2)


if __name__ == '__main__':
    main()

基于greenlet实现多任务

# 首先sudo pip3 install greenlet
import time
from greenlet import greenlet


def test1():
    while True:
        print("----1------")
        gr2.switch()
        time.sleep(0.1)


def test2():
    while True:
        print("----2------")
        gr1.switch()
        time.sleep(0.1)


gr1 = greenlet(test1)
gr2 = greenlet(test2)


def main():
    # 切换到gr1中运行
    gr1.switch()


if __name__ == '__main__':
    main()

# 其实是对yiled进行了封装,启动时候,就是启动某一个,然后进行该函数,该函数会启动切换到其他,实现多任务

基于gevent实现多任务(常用)

# 首先sudo pip3 install gevent

from gevent import monkey
import gevent

# 有耗时操作时需要
monkey.patch_all()  # 将程序中用到的耗时操作代码,换为gevnet中自己实现的模块,这样就可以直接使用time.sleep


# 否则的话,所有的耗时操作例如sleep  socekt接收等等,都要使用gevent专用的函数,很复杂

def f(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)


# gevent其实是对greenlet的再次封装,此处的gr1就是greenlet对象
# gr1 = gevent.spawn(f, 2)
# gr2 = gevent.spawn(f, 2)
#
#
# def main():
#     gr1.join()
#     gr2.join()



# 上面注释部分可以使用下面实现,是等效的
def main():
    gevent.joinall([
        gevent.spawn(f, 2),
        gevent.spawn(f, 2)
    ])


if __name__ == '__main__':
    main()
"""
输出:
<Greenlet at 0x7f9f4a4ce848: f(2)> 0
<Greenlet at 0x7f9f4a4ce848: f(2)> 1
<Greenlet at 0x7f9f4a4cec48: f(2)> 0
<Greenlet at 0x7f9f4a4cec48: f(2)> 1

添加延时之后:
<Greenlet at 0x7f05eebbc848: f(2)> 0
<Greenlet at 0x7f05eebbcc48: f(2)> 0
<Greenlet at 0x7f05eebbc848: f(2)> 1
<Greenlet at 0x7f05eebbcc48: f(2)> 1

得出结论:gevent针对不延时的操作不会进行调度,会先执行完所有才执行其他
但是添加gevent.sleep(0.5)之后,有延时操作之后才是真的调度多任务,gevent中睡眠不能使用time.sleep无效
"""


举报

相关推荐

0 条评论