美文网首页
Python - 多任务

Python - 多任务

作者: huangsv | 来源:发表于2019-07-23 16:38 被阅读0次

一、进程

1、multiprocessing.Process创建进程

import multiprocessing

if __name__ == '__main__':
    p1 = multiprocessing.Process()
    # target:表示一个可调用对象,进程启动时,调用此对象
    # args:表示传递给 target 对象的参数元组
    # name:当前进程的别名
    p1.start()

2、使用Pool进程池创建进程

from multiprocessing import Pool

def task(i):pass

if __name__ == '__main__':
    p = Pool(2)
    print('----开始----')
    for i in range(10):
        p.apply_async(task, args=(i,))
        # task可以看作是target的参数
    p.close()
    p.join()

3、进程间的通信

进程间的通信使用Queue来实现。

import multiprocessing
import time
def write_task(q):
    data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    if not q.full():  # 判断队列是否满了 满了 = True
        for i in data:
            msg = '消息'+str(i)
            q.put(msg)
            # print('写入:' + msg)
        # 打印队列的大小
        print('写入 %d 条数据' % q.qsize())


def read_task01(q):
    time.sleep(1)
    print('开始读取消息......')
    while not q.empty():  # 判断列表是否为空 空 = True
        print('读取:%s' % q.get(True, 2))
        time.sleep(0.4)
        
if __name__ == '__main__':
    # 创建一个队列
    q = multiprocessing.Queue()
    # 在创建进程时,将实例出的Queue当作参数进行传递
    p_w = multiprocessing.Process(target=write_task, args=(q,))
    p_r = multiprocessing.Process(target=read_task01, args=(q,))
    p_w.start()
    p_r.start()
    p_w.join()
    p_r.join()

二、线程

1、threading 创建线程

import threading

if __name__ == '__main__':
    t1 = threading.Thread()
    # target:表示一个可调用对象,线程启动时,调用此对象
    # name:当前线程的名称
    # args:表示传递给 target 对象的参数元组
    # kwargs:传递给 target 对象的参数字典

    # start这里真正的创建线程并启动线程
    t1.start()

2、threading.Thread 创建线程

import threading
import time
# 创建一个子类MyThread子类,继承threading.Thread线程类
class MyThread(threading.Thread):
    # 调用对象 start() 方法时,程序会自动调用run()方法
    def run(self):
        for i in range(5):
           time.sleep(1)
           msg = "子线程" + self.name + "执行, i = " + str(i)
           print(msg)

if __name__ == '__main__':
    print("----主线程开始----")
    t = MyThread()
    t.start()
    t.join()

三、线程间的资源竞争——锁

当多个线程在操作同一个资源时有可能同时在修改一个资源或者变量,这时会对该资源造成冲突,为解决这种冲突,可以使用锁的概念,那个线程抢到资源就把锁住,然后对其进行操作,完成后再释放资源让另一个线程操作资源。这种可以解决资源的冲突问题,不过在开发时应该避免死锁(多个线程间相互等待资源释放资源,造成线程僵死状态)

import threading
'''使用互斥锁解决多线程之间的资源竞争问题'''
# 全局变量
num = 0
# 创建一把锁
mutex = threading.Lock()

def test01(temp):
    print("----子线程1开始----")
    global num
    # 上锁
    mutex.acquire()
    for i in range(temp):
        num += 1
    # 解锁
    mutex.release()
    print("子线程1值:", num)
    print("----子线程1结束----")

def test02(temp):
    print("----子线程2开始----")
    global num
    # 上锁
    mutex.acquire()
    for i in range(temp):
        num += 1
    # 解锁
    mutex.release()
    print("子线程2值:", num)
    print("----子线程2结束----")

def main():
    print("----主线程开始----")
    # 在线程中使用args传递函数参数,args是一个元组
    t1 = threading.Thread(target=test01, args=(1000000,))
    t2 = threading.Thread(target=test02, args=(1000000,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("主线程值:", num)
    print("----主线程结束----")

if __name__ == '__main__':
    main()

四、协程

在了解协程之前首先要了解Python的迭代器,生成器

1、迭代器

import time
from collections import Iterable
from collections import Iterator

class Classmate(object):
    def __init__(self):
        self.names = list()
        self.curren_num = 0

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

    def __iter__(self):
        '''如果想要一个对象称为一个可以迭代的对象,即可以使用for,那么必须实现__iter__方法'''
        return self  # 返回本类中的 __next__方法

    def __next__(self):
        '''成为一个可迭代器必须有的方法,__next__'''
        if self.curren_num < len(self.names):
            ret = self.names[self.curren_num]
            self.curren_num += 1
            return ret
        else:
            raise StopIteration

if __name__ == '__main__':
    classmate = Classmate()
    classmate.add("A1")
    classmate.add("A2")
    classmate.add("A3")

    print("判断classmate是否可以迭代的对象:", isinstance(classmate, Iterable))
    classmate_iterator = iter(classmate)
    print("判断是classmate_iterator是否迭代器:", isinstance(classmate_iterator, Iterator))

    for name in classmate:
        print(name)
        time.sleep(0.5)

说明白一点就是迭代器就是让一个实例出来的类对象可以使用for进行遍历,可以看到最后的for遍历的目标就是对象

2、生成器

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

1)第一种生成器,可以看作一种列表推导式

num = (i * i for i in range(10))
print(num)
for i in num:
    print(i)

2)第二种生成器

def Fibonacci(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        # 使用yield返回数据
        ret = yield a
        print(ret)
        a, b = b, a + b
        current_num += 1
    # 这里的值需要在异常信息中获取(异常.value)
    return "END"

if __name__ == '__main__':
    # 生成器可当作类进行实例化出对象
    obj = Fibonacci(10)
    obj_next = Fibonacci(10)
    
    # send方法抽取值,如果第一次使用需要传入None
    print(obj.send(None))
    # next方法
    print(next(obj))
    # send方法
    print(obj.send("send方法可以传入参数"))  # send方法不能向刚启动的生成器发送值
    
    # next方法 抽取obj_next的全部值
    while True:
        try:
            print(obj_next(obj))
        except Exception as ret:
            # 获得Fibonacci中return的值
            print(ret.value)
            break

如果说Python的列表存储的是 已经得到的数据;那么生成器存储的就是 得到数据的方法,相比之下生成器消耗的资源更少

下面使用yield来实现协程多任务的并发运行

'''使用yield实现的多任务,协程,占用资源最小'''
def task01():
    while True:
        print('---1---')
        yield

def task02():
    while True:
        print('---2---')
        yield

def main():
    t1 = task01()
    t2 = task02()
    while True:
        next(t1)
        next(t2)

if __name__ == '__main__':
    main()

使用yield实现的协程多任务,占用资源最小;可以理解为yield将本内代码暂停一小会儿,而去执行另一个代码

下面使用greenlet来实现协程

from green import greenlet

def task01():
    while True:
        print('---1---'
        # 暂停,去执行gr2
        gr2.switch()

def task02():
    while True:
        print('---2---')
        # 暂停,去执行gr1
        gr1.switch()

def main():
    gr1 = greenlet(task01)
    gr2 = greenlet(task02)
    gr1.switch()

if __name__ == '__main__':
    main()

greenlet使用代码块间的交替来实现的多任务

下面使用gevent来实现协程,这也是目前开发中常用的,推荐

import gevent

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

if __name__ == '__main__':
    g1 = gevent.spawn(task, 5)
    g2 = gevent.spawn(task, 5)
    g3 = gevent.spawn(task, 5)
    g1.join()
    g2.join()
    g3.join()

使用gevent完成可以看作缩小版的线程

总结

1、进程是资源分配的单位,进程不会工作,可以看作进程只是向CPU申请一块资源空间

2、线程是操作系统调度的单位,在CPU中真正工作的是线程

3、进程切换需要的资源最大,效率低

4、线程切换需要的资源一般,效率一般(除GIL)

5、协程切换的任务资源少,效率高

6、多进程、多线程根据CPU核不一样可能是并行的,但是协程是在一个线程中,所以是并发

笔者拙见,如有不对,欢迎指正

相关文章

  • GIL

    Python的GIL是什么鬼 学习编程的时候,我们少会涉及到多任务。可是在python中使用多任务经常会提...

  • Python多线程多进程

    Python多线程多进程 QUICK START 1.[endif]进程和线程 1.1系统多任务机制 多任务操作的...

  • Python3简单实现多任务(线程/协程篇)

    写在前面 上一篇文章[Python3简单实现多任务(多进程篇)]已经介绍了python多进程实现多任务的简单实现方...

  • python——多任务

    linux才是真正的多用户多任务多任务,一般是通过时间片轮转和优先级调度等实现并发 进程 正在运行着的代码 线程 ...

  • Python - 多任务

    一、进程 1、multiprocessing.Process创建进程 2、使用Pool进程池创建进程 3、进程间的...

  • day24系统编程

    1python系统编程 1.1进程 1.1.1多任务的引入 单任务: 多任务: 说明: ·程序执行到os.fork...

  • 2018-11-22进程,线程,协程

    进程:代码+资源,可以实现多任务线程:运行在进程中的最小单元,消耗资源小于进程 可以实现多任务协程:Python独...

  • Python多任务总结

    多任务什么是多任务同时做多件事件(做个多个任务),运行多个方法多任务的原理并发:假的多任务,时间片的轮转,快速的交...

  • python多任务 线程

    什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一...

  • python多任务--进程

    一、进程 进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的...

网友评论

      本文标题:Python - 多任务

      本文链接:https://www.haomeiwen.com/subject/xcjalctx.html