多任务(进程、线程、协程)

python编程快速上手(持续更新中…)

介绍

统一时间有多个任务执行
Python默认单任务

进程和线程、协程对比

进程:是资源分配基本单位
线程:cpu调度基本单位
协程:单线程执行多任务

切换效率:协程>线程>进程

高效方式:进程+协程

选择问题:

多进程:
密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
缺陷:多个进程之间通信成本高,切换开销大。

多线程:
密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。

协程:
当程序中存在大量不需要CPU的操作时(IO),适用于协程;

一个程序至少有一个进程,一个进程至少有一个线程

线程并发度高

线程共享内存,提高运行效率

线程不能独立运行,必须存在进程中

大量重复工作,用进程

cpython解释器

线程

基本用法

主线程:程序启动,创建,其它线程结束,关闭
子线程:主线程可以创建子线程

子线程创建步骤
1.导入threading
2.obj_thread = threading.thread(obj)
3.obj_thread.start()

线程名称、总数量

获取线程数量(活跃):
thread_num = len(threading.enumerate())

获取线程对象中有名称
threading.current_thread()

线程的参数与顺序

参数顺序一致

传参数有3种方法
1、元组传递
threading.Thread(target=sing, args=(10,100,1000))
2、字典传递
thread_sing = threading.Thread(target=sing, kwargs={“a”: 10, “c”: 1000, “b”: 100})
3、混合传递
thread_sing = threading.Thread(target=sing, args=(10, ), kwargs={“c”: 1000, “b”: 10})

线程的执行顺序是无序的

守护线程

表示子线程就守护了主线程(主线程结束后,子线程也结束)
thread_work.setDaemon(True)

并发与并行

并发:任务数大于cpu核数
并行:任务数小于cpu核数

自定义线程类
1、继承threading
2、重写run
3、通过子类对象.start()

共享全局变量

共享全局变量问题
资源竞争问题
t1.join() t1先执行

同步与异步

同步:多任务中,有先后顺序

异步:多任务中,没有先后顺序,多个任务同时执行

锁机制

保证统一时间只有一个任务执行

互斥锁

创建锁
lock1 = threading.Lock()

上锁
lock1.acquire()

解锁
lock1.release()

死锁

  • 死锁:在多线程中,两个线程都占用一些资源 ,而且同一时间都在等待对方释放资源,这种状态就是死锁状态
  • 避免:锁使用完毕后,要及时释放

进程

进程:资源分配的基本单位,也是线程的容器

进程的状态
多任务(进程、线程、协程)_第1张图片

基本使用

1、导入模块
import multiprocessing
2、通过模块提供的Process类创建进程对象
process_obj = multiprocessing.Process(target=work1)
3、启动进程
process_obj.start()

进程名称,总数量

设置进程名称
multiprocessing.Process(target=work1, name=“P1”)

获取进程名称
multiprocessing.current_process()

获取进程编号
multiprocessing.current_process().pid
使用os模块来获取:os.getpid()

Kill -9 进程编号 杀进程

线程的参数传递

1)使用 args 传递元组
2)使用 kwargs 传递字典
3)混合使用 args 和 kwargs

守护进程

#设置线程的setDaemon(True)
#设置 process_obj 子进程守护主进程
#process_obj.daemon = True

#terminate() 终止子进程的执行
process_obj.terminate()

消息队列

基本使用

#1)创建队列(指定长度)
#multiprocessing.Queue(n) n 表示队列长度
queue = multiprocessing.Queue(5)

#2)放值
#queue.put(值) 向队列中放入值
queue.put(1) # 放入1数字
queue.put(“hello”)
queue.put([1,2,3])
queue.put((4,5,6))
queue.put({“a”: 10, “b”: 100})

#长度为5,放入第6个数据后,队列就进行入了阻塞状态,默认会等待队列先取出值再放入新的值
#queue.put(10)
#put_nowait() 表示放入值,如果已满,不再等待,直接报错
#queue.put_nowait(10)

##3)取值
#value = queue.get()
#print(value)
#print("–"*20)

##get_nowait() 当队列已空的时候,不会等待放入新的值,直接报错
#value = queue.get_nowait()
#print(value)
#print("–"*20)

常见判断

#1、判断是否已满
#queue.full() 判断队列是否已满, True 满 False 未满
isFull = queue.full()

#3、取出队列中消息的个数
print(“消息的个数:”, queue.qsize()) #

#2、判断是否已经为空 True 空 False 未空
isEmpty = queue.empty()
print(“isEmpty —>”, isEmpty)

进程间通信

"""
思路:
1、准备两个进程
2、准备一个队列 一个进程向队列中写入数据,然后吧队列传递到另外一个进程
3、另外一个进程读取数据

"""
import time
import multiprocessing


# 1、写入数据到队列的函数
def write_queue(queue):

    # for循环,向队列写入数据
    for i in range(10):
        # 判断队列是否已满
        if queue.full():
            print("队列已满!")
            break
        # 向队列中放入值
        queue.put(i)
        print("写入成功,已经写入:",i)
        time.sleep(0.5)

# 2、读取队列数据并显示的函数
def read_queue(queue):

    while True:

        # 判断队列是否已经为空
        if queue.qsize() == 0:
            print("队列已空!")
            break

        # 从队列中读取数据
        value = queue.get()
        print("已经读取:", value)


if __name__ == '__main__':

    # 3、创建一个空的队列
    queue = multiprocessing.Queue(5)

    # 4、创建2个进程,分别写数据、读数据
    write_queue1 = multiprocessing.Process(target=write_queue, args=(queue, ))
    read_queue1 = multiprocessing.Process(target=read_queue, args=(queue, ))
    write_queue1.start()

    # 优先让写数据进程执行结束后,再读取数据
    # write_queue1.join() 先让写的进程执行完成,再去启动读的进程
    write_queue1.join()

    read_queue1.start()

进程池

管理维护进程

#1)导入模块
#2)创建进程池 multiprocessing.Pool(2) 最大允许创建2个进程
pool = multiprocessing.Pool(3)

同步

#3、先用进程池同步方式拷贝文件
#pool.apply(函数名, (传递给函数的参数1,参数2,…))
#pool.apply(copy_work)

异步

# 4、再用进程池异步工作方式拷贝文件
# 如果使用 apply_async 方式,需要做2点:
# 1)pool.close() 表示不在接收新的任务
# 2)主进程不在等待进程池执行结束后在退出,需要进程池join()
#   pool.join() 让主进程等待进程池执行接收后再退出
pool.apply_async(copy_work)

#pool.close() 表示不在接收新的任务
pool.close()
#pool.join() 让主进程等待进程池执行接收后再退出
pool.join()

进程池中queque

"""
思路:
1、准备两个进程
2、准备一个队列 一个进程向队列中写入数据,然后吧队列传递到另外一个进程
3、另外一个进程读取数据

"""
import time
import multiprocessing


# 1、写入数据到队列的函数
def write_queue(queue):

    # for循环,向队列写入数据
    for i in range(10):
        # 判断队列是否已满
        if queue.full():
            print("队列已满!")
            break
        # 向队列中放入值
        queue.put(i)
        print("写入成功,已经写入:",i)
        time.sleep(0.5)

# 2、读取队列数据并显示的函数
def read_queue(queue):

    while True:

        # 判断队列是否已经为空
        if queue.qsize() == 0:
            print("队列已空!")
            break

        # 从队列中读取数据
        value = queue.get()
        print("已经读取:", value)


if __name__ == '__main__':

    # 1、创建进程池
    pool = multiprocessing.Pool(2)

    # 2、创建进程池中的队列
    queue = multiprocessing.Manager().Queue(5)

    # 3、使用进程池执行任务

    #     3.1 同步方式
    # pool.apply(write_queue, (queue, ))
    # pool.apply(read_queue, (queue, ))

    #     3.2 异步方式
    # apply_async() 返回值 ApplyResult对象,该对象由一个 wait() 的方法
    # wait() 方法类似join() 表示先让当前进程执行完毕,后续进程才能启动
    result = pool.apply_async(write_queue, (queue, ))
    result.wait()

    pool.apply_async(read_queue, (queue, ))
    # close()表示不再接收新的任务
    pool.close()
    # 主进程会等待进程池执行结束后再退出
    pool.join()

协程

可迭代对象及检测方法

迭代 —》遍历
可迭代 —》 可遍历
哪些是可遍历
列表 元组 字符串 字典 range()

不可迭代(遍历)对象
‘int’ object is not iterable
for value in 10:
print(value)

可迭代对象检测

isinstance(待检测的对象, Iterable)
返回值为:True 可以迭代
False 不可以迭代

自定义一个可迭代类
class MyClass(object):

增加一个 __iter__方法
该方法就是一个迭代器
def __iter__(self):
    pass

迭代器作用

1)记录当前迭代的位置
2)配合next() 获取可迭代对象的下一个元素值

迭代器基础使用
#2、获取迭代器
l1_iterator = iter(data_list1)

#3、根据迭代器,可以获取下一个元素
value = next(l1_iterator)
print(value) # 1

自定义迭代器类,满足2点
#1)必须含有 iter()
#2) 必须含有 next()
class MyIterator(object):
def iter(self):
pass

# 当 next(迭代器) 的时候,会自动调用该方法
def __next__(self):
    pass

自定义列表

"""
1、MyList类
1)初始化方法
2)__iter__() 方法,对外提供迭代器
3)addItem() 方法,用来添加数据

2、自定义迭代器类:MyListIterator
1) 初始化方法
2)迭代器方法 __iter__()
3) 获取下一个元素值的方法 __next__()

目标:
mylist = MyList()
for value in mylist:
    print(value)


"""


# 1MyListclass MyList(object):
    # 1)初始化方法
    def __init__(self):

        # 定义实例属性,保存数据
        self.items = []

    # 2__iter__() 方法,对外提供迭代器
    def __iter__(self):

        # 创建MyListIterator 对象
        mylist_iterator = MyListIterator(self.items)
        # 返回迭代器
        return mylist_iterator

    # 3addItem() 方法,用来添加数据
    def addItem(self, data):
        # 追加保存数据
        self.items.append(data)
        print(self.items)

# 2、自定义迭代器类:MyListIterator
class MyListIterator(object):
    # 1) 初始化方法
    def __init__(self, items):

        # 定义实例属性,保存MyList类传递过来的items
        self.items = items

        # 记录迭代器迭代的位置
        self.current_index = 0
    # 2)迭代器方法 __iter__()
    def __iter__(self):
        pass
    # 3) 获取下一个元素值的方法 __next__()
    # next(mylist_iterator) 就会调用 __next__() 方法
    def __next__(self):

        # 1、 判断当前的下标是否越界
        if self.current_index < len(self.items):
        #     1)根据下标获取下标对应的元素值
            data = self.items[self.current_index]
        #     2)下标位置 +1
            self.current_index += 1
        #     3)返回下标对应的数据
            return data

        #  如果越界,直接抛出异常
        else:

            # raise 用于主动抛出异常
            # StopIteration 停止迭代
            raise StopIteration


if __name__ == '__main__':
    # 1、创建自定义列表对象
    mylist = MyList()
    mylist.addItem("张飞")
    mylist.addItem("关羽")
    mylist.addItem("班长")
    mylist.addItem("xxxxx")

    # 遍历
    # 1) iter(mylist) 获取 mylist对象的迭代器  --> MyList --> __iter__()
    # 2next(迭代器) 获取下一个值
    # 3)捕获异常
    # for value in mylist:
    #     print("name:", value)



    mylist_iterator = iter(mylist)
    # value = next(mylist_iterator)
    # print(value)
    #
    # value = next(mylist_iterator)
    # print(value)
    #
    #
    # value = next(mylist_iterator)
    # print(value)
    #
    #
    # value = next(mylist_iterator)
    # print(value)
    #
    # value = next(mylist_iterator)
    # print(value)

生成器

生成器 ,是一个特殊的迭代器(保存位置+返回下一个值)

next(迭代器)得到下一个值
next(生成器) 也能够得到下一个值

生成器创建方式:
1) 列表推导式
2) 函数中使用了 yield

# 生成器的创建一:
data_list2 = (x*2 for x in range(10))
print(data_list2) #<generator object <genexpr> at 0x0000022D82CDF040>

# 通过next获取下一个值
value = next(data_list2)
print("------->", value)


方法二:函数中使用yield 
# def test():
#     return  10
# m = test()
# print("m = ", m)  # ?  10
#
# # 使用yield 创建了一个生成器
# def test1():
#     yield 10
#
# # n 是一个生成器对象
# n = test1()
# print(n)
#
# value = next(n)
# print("----->", value)

生产器注意
Return 可以停止生成器
可以通过参数去判断

协程是什么

协程:在不开辟新的线程的基础上,实现多个任务

协程是一个特殊的生成器

"""
1、创建 work1 的生成器

2、创建 work2 的生成器

3、获取生成器,通过next运行生成器


"""
import time


# 1、创建 work1 的生成器
def work1():
    while True:
        print("正在执行work1...")
        yield
        time.sleep(0.5)


# 2、创建 work2 的生成器
def work2():
    while True:
        print("正在执行work2................")
        yield
        time.sleep(0.5)


# 3、获取生成器,通过next运行生成器
if __name__ == '__main__':

    w1 = work1()
    w2 = work2()

    while True:
        next(w1)
        next(w2)

greenlent

帮我们快速实现协程

"""
greenlet 实现协程的步骤:
1、导入模块
2、创建任务 work1   work2
3、创建 greenlet 对象
4、手动 switch 任务


"""

import time
from greenlet import greenlet


# 1、创建 work1 的生成器
def work1():
    while True:
        print("正在执行work1...")
        time.sleep(0.5)
        # 切换到第二个任务
        g2.switch()


# 2、创建 work2 的生成器
def work2():
    while True:
        print("正在执行work2................")
        time.sleep(0.5)
        # 切换到第一个任务
        g1.switch()


if __name__ == '__main__':

    # 创建greenlet的对象
    # greenlet(函数名)
    g1 = greenlet(work1)
    g2 = greenlet(work2)
    # 执行work1任务
    g1.switch()

gevent

自动切换

"""
gevent 好处:能够自动识别程序中的耗时操作,在耗时的时候自动切换到其他的任务
1、导入模块
2、指派任务

"""
# 一般放到开头处
# 1、导入模块
from gevent import monkey
# 2、破解所有
monkey.patch_all()

import time
import gevent


# 1、创建 work1 的生成器
def work1():
    while True:
        print("正在执行work1...", gevent.getcurrent())
        time.sleep(0.5)
        # 默认情况下 time.sleep() 不能被gevent 识别为耗时操作
        # 1) 把time.sleep() ----> gevent.sleep()
        # 2) 给 gevent 打补丁(目的:让 gevent 识别 time.sleep())
        # 打补丁:
        #   在不修改程序源代码的情况下,为程序增加新的功能
        # 如何打补丁?
        # 1) 导入模块 monkey 模块   from gevent import monkey
        # 2) 破解 monkey.patch_all()
        # gevent.sleep(0.5)


# 2、创建 work2 的生成器
def work2():
    while True:
        print("正在执行work2................", gevent.getcurrent())

        time.sleep(0.5)
        # gevent.sleep(0.5)


if __name__ == '__main__':

    # 指派任务
    # gevent.spawn(函数名, 参数1,参数2,....)
    g1 = gevent.spawn(work1)
    g2 = gevent.spawn(work2)

    # 让主线程等待携程执行完毕再退出
    g1.join()
    g2.join()

你可能感兴趣的:(python,python,数据库,java)