02_多任务

1_线程

00_线程的总结

"""
重点搞清楚什么是多任务,怎样完成多任务
    多任务:
        在一个程序里边,有多个while True,多个函数,多个部分功能不同的代码,想要让他们一起执行,
        或者按照一定顺序(规律)执行就是多任务
    线程:
        实现多任务的一种基本的方式,比较轻量级,简洁(杀鸡用鸡刀,不用牛刀)
        import threading

            t1 = threading.Thread(target=test1,args=(,))
            t2 = threading.Thread(target=test2,元组参数)

            t1.start()
            t2.start()

    多线程共享全局变量:
        多线程共享一个全局变量时,若同时出现多个线程对全局变量进行读写,就可能会出现错误
        解决的方法规律:1.从逻辑的角度讲来避免(让一个人写,其他人都读)
                    2.从程序的角度讲来防止这种情况出现(使用互斥锁(上厕所)思想来避免)

        互斥锁:(定义全局变量)
              mutex.acquire()
              ....*****.....
              mutex.release()

        mutex = threading.Lock()
              将可能出现问题的代码段锁住,多是出现全局变量的部分
              (Python语言中的代码,有时候看上去是一句,实际上真正让操作系统执行时要翻译成很多句),
              锁的代码段越少越好,实现了不管哪个方法先被调用,只要有一个调用了会导致另一方调用失败

        死锁:
            你等我解锁资源,我等你解锁资源,互相等,都不肯先解
            解决方法:
                    1.程序设计是尽量避免(银行家算法:先都不满足,再从少到多逐步满足)
                    2.添加超时时间

"""

01_没有多任务的代码

import time


def sing(name):
    for i in range(5):
        print("%s要唱歌..." % name)
        time.sleep(1)


def dance(name):
    for i in range(5):
        print("%s要跳舞..." % name)
        time.sleep(1)


def main():
    name = input("请输入要唱歌的人:")
    sing(name)
    dance(name)
if __name__ == '__main__':
    main()

02_多任务_线程_demo

import time
import threading
"""阅读一个程序切忌从上往下一行一行地看,要弄清楚程序先干什么,再干什么
    eg:c语言从main()开始看起
"""
"""主线程自上向下运行,当运行到threading.Thread()时,创建一个对象,当这个对象调用start()时
   主线程创建一个子线程,子线程开始执行Thread()内部传递给target的函数,则这个函数就可以单独执行
"""


def sing():
    for i in range(5):
        print("%s要唱歌...")
        time.sleep(1)


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


def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    # 此处传递函数时,千万不能加括号
    # 只写函数名不加括号--告诉函数位置
    # 加括号---调用函数
    t1.start()
    t2.start()

if __name__ == '__main__':
    main()

03_查看线程数

import threading
import time


def dage():
    for i in range(5):
        print("我不做大哥好多年...")
        # time.sleep(1)
        # 在此处调用是子线程t1:dage暂停等待
        

def dama():
    for i in range(5):
        print("我不做大妈好多年...")
        # time.sleep(1)
        # 在此处调用是子线程t2:dama暂停等待


def main():
    t1 = threading.Thread(target=dage)
    t2 = threading.Thread(target=dama)

    t1.start()
    # 线程创建出来以后谁先执行是不确定的,用time.sleep()来控制顺序,主线程最后结束,
    # 因为主线程结束后整个程序结束即所有的子线程结束

    time.sleep(1)
    # 在此处调用是主线程暂停等待
    # 一秒钟对于cpu来说非常长,足以等到cpu将其他线程执行完
    # 暂停执行调用线程达到给定的秒数, 起控制线程执行与停止等待的作用, 若想暂停一个线程,
    # 就在该线程的执行路线上(由上到下)使用time.sleep(secs), 该线程会停止等待其他线程先执行。
    print("调用t1结束...")

    t2.start()

    time.sleep(1)
    # 在此处调用是主线程暂停等待
    print("调用t2结束...")
    print(threading.enumerate())

if __name__ == "__main__":
    main()



04_查看线程数_循环查看当前运行的线程数量

import threading
import time


def dage():
    for i in range(5):
        print("我不做大哥好多年...")
        time.sleep(1)
        # 在此处调用是子线程t1:dage暂停等待
    # 如果创建Thread时指定的函数运行结束,那么意味着这个子线程结束了
        

def dama():
    for i in range(10):
        print("我不做大妈好多年...")
        time.sleep(1)
        # 在此处调用是子线程t2:dama暂停等待


def main():
    t1 = threading.Thread(target=dage)
    t2 = threading.Thread(target=dama)

    t1.start()
    t2.start()
    while True:
        print(threading.enumerate())
        # 在无限循环条件下,使用睡眠,起到控制语句多长时间执行一次的作用
        if len(threading.enumerate()) == 1:
            break
        time.sleep(1)

if __name__ == "__main__":
    main()



05_验证创建线程的时间以及运行时间

import threading
import time


def dage():
    for i in range(5):
        print("我不做大哥好多年...")


def main():
    # 在调用threading.Thread之前打印当前线程信息
    print(threading.enumerate())
    print("线程个数:%d" % len(threading.enumerate()))
    t1 = threading.Thread(target=dage)
    # 在调用threading.Thread之后打印当前线程信息
    print(threading.enumerate())
    print("线程个数:%d" % len(threading.enumerate()))
    t1.start()
    # 在调用start()之前打印当前线程信息
    print(threading.enumerate())
    print("线程个数:%d" % len(threading.enumerate()))
    

if __name__ == "__main__":
    main()



06_函数里边修改全局变量


num = 100
nums = 200
num2 = [11,22]
"""对于全局变量的修改什么时候需要加global,什么时候不需要的讨论
    在一个函数中对全局变量进行修改的时候,到底是否需要global进行说明,要看是否对全局变的
    指向进行了修改,如果修改了指向,即让全局变量指向了一个新的地方,则必须使用global
    如果仅仅是修改了指向的空间内部的数据,此时不用必须使用global
    引用全局变量,不需要golbal声明,修改全局变量,需要使用global声明,特别地,列表、字典等
    如果只是修改其中元素的值,可以直接使用全局变量,不需要global声明。
"""


def test1():
    """使用global"""
    global num
    num += 100


def test2():
    """不使用global"""
    # nums += 100  出错!
    num2.append(33)

print(num)
print(nums)
print(num2)

test1()
test2()

print(num)
print(nums)
print(num2)

07_线程共享全局变量

import threading
import time
"""线程共享全局变量,子线程和子线程(多线程之间)之间共享全局变量"""

# 定义一个全局变量
g_num = 100


def test1():
    global g_num
    g_num += 100
    print("****in test1 g_num = %d " % g_num)


def test2():
    print("****in test2 g_num = %d " % g_num)


def main():
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    t1.start()
    time.sleep(1)
    # 不知道两个函数哪一个先执行,故使用此方法,让test1先执行
    t2.start()
    time.sleep(1)
    print("****in main_Thread g_num = %d" % g_num)

if __name__ == '__main__':
    main()

08_多线程共享全局变量

import threading
import time
"""
    线程共享全局变量,子线程和子线程(多线程之间)之间共享全局变量
    共享的意义:多任务一般都是配合执行的
"""

# 定义一个全局变量
g_num = 100


def test1(temp):
    """有实参传递过来的时候,要定义形参"""
    temp.append(33)
    print("****in test1 temp = %s " % str(temp))


def test2(temp):
    print("****in test2 temp = %s " % str(temp))

g_nums = [11,22]


def main():
    # target 指定将来这个线程去哪个函数执行代码
    # args 指定将来调用函数的时候,传递什么数据过去,传递什么都可以,必须以元组的形式传递
    t1 = threading.Thread(target=test1, args=(g_nums,))
    t2 = threading.Thread(target=test2, args=(g_nums,))
    t1.start()
    time.sleep(1)
    # 不知道两个函数哪一个先执行,故使用此方法,让test1先执行
    t2.start()
    time.sleep(1)
    print("****in main_Thread g_nums = %s" % str(g_nums))

if __name__ == '__main__':
    main()

09_线程共享全局变量的问题

import threading
import time
"""线程共享全局变量,子线程和子线程(多线程之间)之间共享全局变量"""

# 定义一个全局变量
g_num = 0


def test1(num):
    global g_num
    for i in range(num):
        g_num += 100
    print("****in test1 g_num = %d " % g_num)


def test2(num):
    global g_num
    for i in range(num):
        g_num += 100
    print("****in test2 g_num = %d " % g_num)


def main():
    t1 = threading.Thread(target=test1,args=(1000000,))
    t2 = threading.Thread(target=test2,args=(1000000,))
    # 传入的args的值越大越容易出错误,多个线程同时写入一定会出问题!,问题在于一个线程还没有做完
    # 就开始执行下一个了(图1)
    t1.start()
    t2.start()

    # 延时目的:等待上边两个线程执行完毕
    time.sleep(2)
    print("****in main_Thread g_num = %d" % g_num)

if __name__ == '__main__':
    main()

10_使用互斥锁解决资源竞争的问题-1

import threading
import time
"""线程共享全局变量,子线程和子线程(多线程之间)之间共享全局变量"""

# 定义一个全局变量
g_num = 0


def test1(num):
    # 上锁,如果之前没有被上锁,那么此时上锁成功
    # (去上厕所,发现门开着,没锁,说明里边没人,进去锁门上厕所)
    # 如果上锁之前已经被上锁了,那么此时会堵塞在这里,直到这个锁被解开为止
    # (相当于去厕所,发现已经有人在里边把锁锁上了,只能在外边等着)
    mutex.acquire()
    global g_num
    for i in range(num):
        g_num += 1
    print("****in test1 g_num = %d " % g_num)
    # 解锁
    mutex.release()


def test2(num):
    mutex.acquire()
    global g_num
    for i in range(num):
        g_num += 1
    print("****in test2 g_num = %d " % g_num)
    mutex.release()

mutex = threading.Lock()
"""
上互斥锁,在所有可能会出现资源竞争的地方使用互斥锁(mutex.acquire();mutex.release())
将其套起来.使程序同时只有一个线程在做
互斥锁之上厕所思维:
等于多个人抢着去上厕所,第一个抢到的人先进去,上锁,其他人只能在外边等待,
等第一个人上完再抢,以此类推
"""


def main():
    t1 = threading.Thread(target=test1,args=(1000000,))
    t2 = threading.Thread(target=test2,args=(1000000,))
    # 传入的args的值越大越容易出错误,多个线程同时写入一定会出问题!,问题在于一个线程还没有做完
    # 就开始执行下一个了(图1)
    t1.start()
    t2.start()

    # 延时目的:等待上边两个线程执行完毕
    time.sleep(2)
    print("****in main_Thread g_num = %d" % g_num)

if __name__ == '__main__':
    main()

11_使用互斥锁解决资源竞争的问题-2

import threading
import time
"""线程共享全局变量,子线程和子线程(多线程之间)之间共享全局变量"""

# 定义一个全局变量
g_num = 0


def test1(num):
    global g_num
    for i in range(num):
        mutex.acquire()
        g_num += 1
        mutex.release()
        # 锁住中间这一句话,会导致循环执行一次之后,会再一次抢着上锁,至于谁能抢到是不一定的
        # 有可能.不管是你执行完1000000次我再执行1000000次还是你一次我一次,最终的结果是不会改变的
        # eg:test2执行了920769次,test1执行了1000000次,即test1输出g_num = 1920769
        # test2输出g_num = 2000000
    print("****in test1 g_num = %d " % g_num)


def test2(num):
    global g_num
    for i in range(num):
        mutex.acquire()
        g_num += 1
        mutex.release()
    print("****in test2 g_num = %d " % g_num)


mutex = threading.Lock()
"""
上互斥锁,在所有可能会出现资源竞争的地方使用互斥锁(mutex.acquire();mutex.release())
将其套起来.使程序同时只有一个线程在做,上锁的原则:被套的代码越少越好
互斥锁之上厕所思维:
等于多个人抢着去上厕所,第一个抢到的人先进去,上锁,其他人只能在外边等待,
等第一个人上完再抢,以此类推
"""


def main():
    t1 = threading.Thread(target=test1,args=(1000000,))
    t2 = threading.Thread(target=test2,args=(1000000,))
    # 传入的args的值越大越容易出错误,多个线程同时写入一定会出问题!,问题在于一个线程还没有做完
    # 就开始执行下一个了(图1)
    t1.start()
    t2.start()

    # 延时目的:等待上边两个线程执行完毕
    time.sleep(2)
    print("****in main_Thread g_num = %d" % g_num)

if __name__ == '__main__':
    main()

12_案例:多任务UDP聊天器

import socket
import threading


def send_masg(udp_socket,dest_addr):
    """发送数据  功能独立,单独封装"""
    while True:

        # 获取发送信息
        send_data = input("请输入要发送的数据:")
        # 发送信息
        udp_socket.sendto(send_data.encode("utf-8"),dest_addr)
        #udp_socket.sendto(send_data.encode("utf-8"), ("192.168.146.130",8080))


def recv_masg(udp_socket):
    """接受并显示数据"""
    while True:
        recv_data = udp_socket.recvfrom(1024)
        print("接收到的数据是:%s" % str(recv_data[0].decode("utf-8")))


def main():
    # 定义套接字
    udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    # 绑定本地信息
    udp_socket.bind(("",7890))

    # 获取对方ip和port
    # 在发送函数外部获取,当函数无限循环时,就不用重复获取了
    dest_ip = input("请输入要发送目标的ip:")
    dest_port = int(input("请输入要发送目标的port:"))
    dest_addr = (dest_ip,dest_port)

    # 接收/发送信息
    # 若使用单线程,会在各个while True内部形成死循环,出不来
    # 故使用两个线程(一主一子),分开各自执行
    t_send_masg = threading.Thread(target=send_masg,args=(udp_socket,dest_addr))
    t_recv_masg = threading.Thread(target=recv_masg,args=(udp_socket,))
    t_send_masg.start()
    t_recv_masg.start()

if __name__ == '__main__':
    main()

2_进程

00_进程总结

"""
 1.进程:一个程序运行起来后,代码+用到的资源称为进程,它是操作系统分配资源的基本单元    进程内部就可以实现多任务,但到底是谁去做呢?
        其实进程仅仅是系统进行资源分配和调度的单位,线程拿着进程的资源实现多任务

 2.多任务的实现形式有三种:[流水线思想]
        形式一--多进程实现多任务:
                一个进程内部有一个主线程,从上到下逐步执行,当主线程调用函数或者方法的时候,就在内部
                开辟一个新的进程,将原进程中的代码资源都复制过去.形成多个进程同时进行,每个进程内部
                仅含有一个主线程
        形式二--多线程执行多任务:
                一个进程内部有多个线程在执行,一部分完成后再进行下一部分(同时执行)
        形式三--多线程多进程实现多任务:
                同时有多个进程在执行,每个进程内部有多个线程在执行,每个进程间是彼此独立的

 3.多使用多线程,多进程少用,太浪费资源

 4.进程的使用:
        实现多任务的另一种基本的方式,比较浪费资源
        import multiprocessing

            p1 = multiprocessing.Process(target=test1,args=(,))
            p2 = multiprocessing.Process(target=test2,元组参数)

            p1.start()
            p2.start()

 5.进程之间不共享全局变量

 6.多进程之间通过Queue来实现数据共享(在同一个程序内部,同一台电脑上)
        创建队列
        q = multiprocessing.Queue()
        将队列以元组参数的形式传递给子进程
        p1 = multiprocessing.Process(target=Download_from_web, args=(q,)
        在不同的子进程中对队列进行添加和获取
        q.put(i)
        data = q.get()
"""

01_多任务_进程_demo

import time
import threading
import multiprocessing
"""阅读一个程序切忌从上往下一行一行地看,要弄清楚程序先干什么,再干什么
    eg:c语言从main()开始看起
"""
"""主线程自上向下运行,当运行到threading.Thread()时,创建一个对象,当这个对象调用start()时
   主线程创建一个子线程,子线程开始执行Thread()内部传递给target的函数,则这个函数就可以单独执行
"""


def sing():
    for i in range(5):
        print("%s要唱歌...")
        time.sleep(1)


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


def main():
    # t1 = threading.Thread(target=sing)
    # t2 = threading.Thread(target=dance)
    #
    # t1.start()
    # t2.start()
    p1 = multiprocessing.Process(target=sing)
    p2 = multiprocessing.Process(target=dance)
    p1.start()
    p2.start()
if __name__ == '__main__':
    main()

02_获取进程的pid

import time
import multiprocessing
import os  # 操作系统的接口模块


def test():
    while True:

        print("子进程的pid = %d" % os.getpid())
        time.sleep(2)


def main():
    print("主进程的Pid = %d" % os.getpid())  # getpid():获取当前进程的进程号(ID)
    p = multiprocessing.Process(target=test)
    p.start()

if __name__ == '__main__':
    main()

03_进程的执行顺序

import time
import multiprocessing
import os  # 操作系统的接口模块


def test():
    while True:
        print("子进程的 pid = %d" % os.getpid())
        time.sleep(2)


def test2():
    while True:
        print("子进程2 的pid = %d" % os.getpid())
        time.sleep(2)


def main():
    print("主进程的Pid = %d" % os.getpid())  # getpid():获取当前进程的进程号(ID)
    p1 = multiprocessing.Process(target=test)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()

if __name__ == '__main__':
    main()

04_给Process传递参数

import time
import multiprocessing
import os  # 操作系统的接口模块


def test(a, b, c, *args, **kwargs):
        print("子进程的 pid = %d" % os.getpid())
        print(a)
        print(b)
        print(c)
        print(args)
        print(kwargs)


def main():
    print("主进程的Pid = %d" % os.getpid())  # getpid():获取当前进程的进程号(ID)
    p1 = multiprocessing.Process(target=test, args=(11, 22, 33, 44, 55), kwargs={"mm": 111})
    p1.start()


if __name__ == '__main__':
    main()

05_多进程之间不共享全局变量

import time
import multiprocessing
import os  # 操作系统的接口模块


def test():
    while True:
        nums.append(44)
        print("子进程的pid = %d" % os.getpid())
        print("在子进程1中 nums = %s" % str(nums))
        time.sleep(10)


def test2():
    print("在子进程2中 nums = %s" % str(nums))

nums = [11,22,33]


def main():
    print("主进程的Pid = %d" % os.getpid())  # getpid():获取当前进程的进程号(ID)
    p1 = multiprocessing.Process(target=test)
    p1.start()

    # time.sleep(2)
    p1.join()

    p2 = multiprocessing.Process(target=test2)
    p2.start()

if __name__ == '__main__':
    main()

06_多进程之间通过Queue实现数据共享

import multiprocessing
"""
两个进程之间使用队列链接,一个下载数据并往队列里边写数据,一个从队列里读取数据进行处理
只能用于同一个程序内部,同一个电脑上
"""


def Download_from_web(q):
    """下载数据"""
    # 模拟从网上下载数据
    data = [11, 22, 33, 44, 55, 66]
    # 向队列中写入数据
    for i in data:
        q.put(i)
    print("下载器已经下载完了数据并且存入队列中****")


def analysis_data(q):
    """处理数据"""
    waitting_analysis_data = list()  # 创建一个列表可以用[],也可以list()(这样好)
    # 从对列中获取数据
    while True:
        data = q.get()
        waitting_analysis_data.append(data)

        if q.empty():
            break
    # 模拟数据处理
    print(waitting_analysis_data)


def main():
    # 1.创建一个队列
    q = multiprocessing.Queue()  # 队列内存放多少数值,就在括号内写几,不写默认最大,根据系统算出
    # 2.创建多个进程,将队列的引用当做实参传递进去
    p1 = multiprocessing.Process(target=Download_from_web, args=(q,))
    p2 = multiprocessing.Process(target=analysis_data,args=(q,))
    p1.start()
    p2.start()

if __name__ == '__main__':
    main()

07_创建进程池

from multiprocessing import Pool
import time
import os
import random


def worker(msg):
    # 获取当前时间
    t_start = time.time()
    # 进程池可以放3个进程,所以一开始执行3个获取一次时间,再往后,每完成一个进程添加一个,获取一次时间
    print("%s 开始执行,进程号为:%d" % (msg, os.getpid()))

    # random.random()随机生成0-1之间的浮点数
    time.sleep(random.random()*2)
    # 获取当前时间
    t_stop = time.time()
    print(msg,"执行完毕,耗时%0.2f" % (t_stop-t_start))


def main():
    # 定义一个进程池,最大进程数为3
    po = Pool(3)
    # 创建进程池时为添加任何任务
    for i in range(0,10):
        # 添加了10个任务,大于3个,但不会因为进程满了而添加失败,而是会等待,执行完一个添加一个
        # Pool().appli_async(要调用的目标函数名,(传递给目标的参数元组,))
        # 每次循环将会用空闲出来的子进程去调用目标
        po.apply_async(worker,(i,))
        # 如何将任务添加到进程池中:创建的进程池变量调用apply_async()方法

    print("-----start------")
    # 关闭进程池,关闭后po不再接收新的请求
    po.close()
    # 等待po中所有子进程执行结束,必须放在close()后边
    po.join()
    # 使用Process创建进程,主进程会等待子进程结束再执行,但是使用Pool进程池创建进程,主进程不会等待
    # 必须调用join()方法主动使主进程堵塞,使其等待
    print("------end-------")

if __name__ == '__main__':
    main()

08_案例:多任务文件夹copy-v1

"""
文件夹的拷贝:
创建文件夹;
接下来创建的文件只要路径包含文件夹前边的那个文件,那么就在这个文件夹里面
获取要复制的文件夹的名字
接下来把要复制的文件夹的名字,混上将来想要的新文件夹的名字创建一个新的文件夹
接下来把这个文件夹里边的文件名按照文件名打开,把数据读取出来放入另一个文件夹里边去
单个文件的拷贝:
1.用open()打开一个文件
2.再用open()创建一个文件
3.从原文件内部读取数据
4.将读取的数据写入新文件中去


最终思路:
获取要复制的文件夹的名字
创建一个新的文件夹
获取文件夹中所有的待拷贝的文件的名字
创建进程池,由进程池一个一个往进程内输送要拷贝的文件
接下来把这个文件夹里边的文件按照单个文件的拷贝方式,采用多任务的方式写入另一个文件夹中去
    单个文件的拷贝:
    1.用open()打开一个文件,采用读的方式讲文件中的内容读出来
    2.再用open()创建一个文件,采用写的方式将读到的内容写到新建文件中
    3.从原文件内部读取数据
    4.将读取的数据写入新文件中去



def main():
     要实现的效果:输入一个文件夹的名字,接下来程序就开始自己拷贝,拷贝结束后返回:拷贝成功
     1.获取用户要copy的文件夹的名字

     2.创建一个新的文件夹(接下来要复制原文件夹中的东西到这个新文件夹中来,应该打开原文件夹中的
     文件,把里边的数据读出来,用open()新建一个文件在新的文件夹里,接下来把数据读出来写入新的文
     件夹就可以了.关键是如何知道原文件夹中的文件名,用ipython3验证知识点)

     3.获取文件夹中所有的待拷贝的文件的名字(目的是为了拷贝) 使用listdir()获取

     4.创建进程池,将任务放在进程池中的某个进程里边去,接下来这个进程就单独可以复制了
     主进程负责向这个进程池中添加任务,进程池中的进程就负责完成文件的拷贝,从而实现了解耦

     4.复制原文件夹中的文件,到新的文件夹中的文件去(使用多任务来处理,应如何创建进程,若listdir()
     返回了10000个文件名,创建一万个进程去复制,必死无疑,应该借助进程池的重复利用的特性实现
     一个进程复制完了一个文件,可以让这个进程再去复制下一个文件)

    pass

if __name__ == '__main__':
    main()
"""
import os
import multiprocessing


def copy_file(file_name, old_folder_name, new_folder_name):
    """完成文件的拷贝"""
    print("----模拟copy文件:%s 从文件名:%s---到文件名:%s" % (file_name, old_folder_name, new_folder_name))
    # 到这里不知怎么往下写,可以先把流程给它走通,传一个参数进去看能否正确打印,如可以,则再往里边
    # 传很多个参数就同样可以,但是,尴尬的是在这里进程池里边打印的东西可能无法输出看见,当主进程没有
    # 等待而且执行的比子进程还快的时候就看不见了.为了保证进程池中的子进程执行结束后主进程再执行
    # 使用join()方法,注意:使用前必须先调用close()

    # 在执行过程中,若进程池中的进程产生了异常,则不会显示异常

    old_f = open(old_folder_name + "/" + file_name, "rb")
    # 这是路径的问题:必须要拼路径  要打开的是当前程序(08_案例:多任务文件夹copy.py)所在的路径
    # 下的这个文件夹里边的某个文件,所以要拼路径?
    # w为什么要加一个"/",因为要的是拼出一个路径来:old_folder_name/file_name  路径的格式里边
    # 在进入下一层文件时通常含有"/"
    content = old_f.read()
    # 讲读取的内容保存在变量中,写入的时候用
    old_f.close()
    # 打开了一定要关闭

    new_f = open(new_folder_name + "/" + file_name, "wb")
    new_f.write(content)
    new_f.close()


def main():
    # 1.获取用户要copy的文件夹的名字
    old_folder_name = input("请输入要copy的文件夹的名字:")

    # 2.创建一个新的文件夹
    try:
        new_folder_name = old_folder_name + "复件"
        os.mkdir(new_folder_name)
    except:  # 后边没有任何异常类型,是否是默认匹配任何异常
        pass
    # 保证文件必存在,不存在就创建,存在就啥也不干
    # 每修改一次程序运行的时候就会创建一个 test复件 同名还出错,十分麻烦.实质就是:产生异常
    # 解决方法:使用try:     except: 不让他产生异常
    # 以后写代码不要想着一口气写完,是不可能的,程序是一边测试一边写,一边写一边测试的,用什么写什么

    # 3.获取文件夹中所有的待拷贝的文件的名字
    file_names_list = os.listdir(old_folder_name)
    print(file_names_list)
    # 因为都在同一个文件夹里边,故路径只写文件名就行

    # 4.创建进程池
    pool = multiprocessing.Pool(5)  # 最多有5个进程一起拷贝

    # 5.向进程池中添加 copy文件的任务
    for file_name in file_names_list:
        pool.apply_async(copy_file, args=(file_name, old_folder_name, new_folder_name))
        # 在添加任务时,同时要告诉子进程,拷贝谁(在哪个路径里,即哪个文件夹下的哪个文件),
        # 从哪里拷贝到哪里
    # 复制原文件夹中的文件,到新的文件夹中的文件去
    pool.close()
    pool.join()

if __name__ == '__main__':
    main()

09_案例:多任务文件夹copy-v2

import os
import multiprocessing
"""
添加任务进度:是再添加一个任务来显示进度,还是让主进程来显示?
主进程,反正闲着也是闲着
"""

"""
代码不是一蹴而就的,是经过不断地演变,不断地优化更新形成的,一个版本一个版本做
代码最重要的就是流程,先干什么再干什么
"""


def copy_file(q, file_name, old_folder_name, new_folder_name):
    """完成文件的拷贝"""
    # print("----模拟copy文件:%s 从文件名:%s---到文件名:%s" % (file_name, old_folder_name, new_folder_name))
    old_f = open(old_folder_name + "/" + file_name, "rb")
    content = old_f.read()
    old_f.close()

    new_f = open(new_folder_name + "/" + file_name, "wb")
    new_f.write(content)
    new_f.close()

    # 如果拷贝完了文件,那么就向队列中写入一个消息,表示已经完成
    q.put(file_name)


def main():
    # 1.获取用户要copy的文件夹的名字
    old_folder_name = input("请输入要copy的文件夹的名字:")

    # 2.创建一个新的文件夹
    try:
        new_folder_name = old_folder_name + "复件"
        os.mkdir(new_folder_name)
    except:
        pass

    # 3.获取文件夹中所有的待拷贝的文件的名字
    file_names_list = os.listdir(old_folder_name)
    # print(file_names_list)

    # 4.创建进程池
    pool = multiprocessing.Pool(5)

    # 5.创建队列
    q = multiprocessing.Manager().Queue()

    # 6.向进程池中添加 copy文件的任务 同时将队列作为参数传进去
    for file_name in file_names_list:
        pool.apply_async(copy_file, args=(q, file_name, old_folder_name, new_folder_name))

    # 复制原文件夹中的文件,到新的文件夹中的文件去
    pool.close()
    # pool.join()
    # 由于要显示进度,所以肯定会在拷贝完成之后主进程才会结束,所以就不要join()来睡眠等待了
    # 为了防止主进程提前结束,只用while True使其一直执行
    all_file_number = len(file_names_list)  # 测试一下所有文件个数
    copy_ok_nums = 0
    while True:
        # 等着,等着子进程拷贝完了一个就告诉我一下,然后我就+1.
        # 如何实现上述功能呢:进程间通信(Queue),谁拷贝完了谁就向队列里边写东西,然后用主进程
        # 从队列往外收,收一个就知道拷贝完了一个.通过队列将二者之间的关系关联起来
        """
        显示进度实现思路:
                创建队列:multiprocessing.Manager().Queue() # 通过Manage()创建对象来调用Queue()方法
                q = multiprocessing.Manager().Queue()
                将队列作为一个参数传递到进程池内部,函数接收一下参数(此类参数多在args=内部传递)
                pool.apply_async(copy_file, args=(q, file_name, old_folder_name, new_folder_name))
                copy_file(q,file_name, old_folder_name, new_folder_name)
                如果拷贝完了文件,那么就向队列中写入一个消息,表示已经完成
                q.put(file_name) # 子进程调用添加方法知道了拷贝完了谁
                q.get()  # 主进程调用获取方法
        """
        file_name = q.get()
        # print("已经完成copy: %s" % file_name)
        copy_ok_nums += 1
        # print("已完成:%d/%d" % (copy_ok_nums,all_file_number))
        print("\r已完成:%.2f%%" % (copy_ok_nums*100/all_file_number),end="") # 有除法,加个括号
        # 两个%显示一个%  .2f只留两位小数 print()加上 end=""  不换行  \r:回到行首
        if all_file_number == copy_ok_nums:
            break

    print()
    # 在程序最后的输出留一个空行



if __name__ == '__main__':
    main()

3_协程

00_协程总结

"""
迭代器,生成器,了解概念即可  能干什么? 怎么实现的?

迭代器:
        一大特点:保证了可以使用极少的代码和极小的空间可以生成想要的数据
        可以迭代对象说直白一点就是可以用for循环这种方式
        如何迭代的:取的是可以迭代的这个对象的里边的某个特殊函数的返回值,这个返回值返回的是一个迭代器
        什么叫做迭代器:拥有__iter__()方法和__next__()方法的对象叫做迭代器
        核心点:生成器保存的是生成数据的代码,不是生成数据的结果,通过迭代器可以保证,我可以有一些
        代码,这些代码是用来生成一个值的,什么时候调用我就什么时候生成减少内存空间,如果要是使用列
        表的话,需要先生成所需要数据,存入到列表中,再用for循环遍历取值占用大量的内存空间

生成器:
        最大的特点:含有yield,可以让函数的执行先暂停,同时保留暂停时的值,再次调用时继续向下执行
        核心点:生成器保存的是生成数据的代码,不是生成数据的结果,可以保证函数只执行一部分反而
        返回只要在一个函数中有yield,那么这个函数就不再叫函数而是叫做生成器(模版)
        写一个函数名,原来是调用函数,现在变成了创建一个生成器对象
        生成器中可以没有__iter__()和__next()__,但依旧是迭代器,故称其为特殊的迭代器
        又因为要是迭代器就可以用next()启动,并获取值,所以生成器也可以使用next输出

        yield与 return 的区别:使用return后边的值也会返回,但是函数就相当于调用结束了
"""

01_自己实现一个可以迭代的对象-解析版

import time
from collections import Iterable
from collections import Iterator


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

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

    def __iter__(self):
        """
            要想让自己创建出来的实例对象可以使用for  in  ,
            只需要在自己定义的类中添加一个实例方法 __iter__(self)方法即可
        """
        # return 一个对象的引用,同时这个对象内部必须含有__iter__()方法和__next__()方法
        return ClassIterator()  # 类名加个括号,表示创建了一个实例对象,相当于把这个实例对象的引用返回
        # 对象:是内存中实实在在存在的,是在内存中有一个地址存放他的内容的
        # 引用:可以理解成对这个对象的地址,或者这个对象的名字  eg: a = 1  1是对象,a是这个对象的引用
        # 在python中有两个运算符 == 和 is,前者用于比较对象的值,后者用于比较两个对象是不是同一个


class ClassIterator(object):
    """通过这个类创建出来的对象就可以作为__iter__()方法的返回值了"""
    def __iter__(self):
        pass

    def __next__(self):
        return 11


def main():
    """
        想要用for循环的思路:
                         1.先看看in后边的对象是否可以迭代(基本上创建这个对象的类中使用了__iter__()这个方法就一定可以迭代)
                         2.这个方法是否返回一个具有__iter__(self)方法和__next__(self)方法的对象的引用
                         3.调用iter()函数得到对象classmate的__iter__()方法的返回值
                            (调用iter()函数就会自动调用创建对象classmate的类中的__iter__()方法,同时产生一个返回值)
                         4.__iter__方法的返回值是一个迭代器
                         5.只要有了迭代器,for循环就会通过调用迭代器内部的__next__()来取值(通过调用next()函数会自动调用
                            指定的next()函数内部的对象的这个方法(__next__()方法)),调一次取一个
    """
    classmate = Classmate()
    classmate.add("王五")
    classmate.add("刘五")
    classmate.add("孙五")

    # print("判断对象 classmate 是否是可迭代的对象:", isinstance(classmate, Iterable))
    # 调用iter()函数得到对象classmate的__iter__()方法的返回值,是一个迭代器
    # classmate_iterator = iter(classmate)
    # print("判断对象 classmate_iterator 是否是可迭代器:", isinstance(classmate_iterator, Iterator))
    # 调用next()函数会自动的调用指定的next()函数内部的对象的这个方法(__next__()方法)会返回一个值
    # classmate_iterator是一个迭代器的对象(ClassIterator(object)类创建的对象)
    # print(next(classmate_iterator))

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

if __name__ == "__main__":
     main()

02_自己实现一个可以迭代的对象-解析版2

import time
from collections import Iterable
from collections import Iterator


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

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

    def __iter__(self):
        # return 一个对象的引用,同时这个对象内部必须含有__iter__()方法和__next__()方法
        return ClassIterator(self)  # 类名加个括号,表示创建了一个实例对象,相当于把这个实例对象的引用返回


class ClassIterator(object):
    """通过这个类创建出来的对象就可以作为__iter__()方法的返回值了"""
    def __init__(self, obj):  # obj指向了上一个类创建的对象
        self.obj = obj  # 意味着我找了一个属性,指向了上一个类创建出来的实例对象了

    def __iter__(self):
        pass

    def __next__(self):
        # 在这里实现将王五,刘五,孙五,每次返回一个
        # 如何在这个方法中够到要输出对象的列表?
        # 在上一个类中,已经创建了这个类的一个实例对象了,我想要用你的列表了,我只要能够到你的这个对象
        # 就能用你的列表了,怎样才能够到呢?
        # 在创建实例对象的时候如果可以写上上一个类的对象的引用(即把上一个类的self写进去):就是实现了
        # 把我自己的引用给了你一份,即你就可以在内存中找到列表所在的地址.
        # 上边传了一份引用过来(相当于传了一个实参过来,要在初始化方法中定义一个形参接收),
        # 一定要有一个方法来接收一下--__init__(self)方法
        return self.obj.names[0]


def main():
    """
        在真正的开发过程中,往往出现一种场景,我创建了一个对象,对象里边有个列表属性,列表内部放了
        我往里边添加的很多东西,那么我能不能用for循环的思路来去用列表里边的值呢?
            1.在创建这个对象的类里边必须实现__iter__()方法
            2.__iter__方法必须返回一个迭代器的对象
                    一个类中含有__iter__(self)方法和__next__(self)方法就可以
                    称为迭代器(可以取对象里边的值)

    """
    classmate = Classmate()
    classmate.add("王五")
    classmate.add("刘五")
    classmate.add("孙五")

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

if __name__ == "__main__":
     main()

03_自己实现一个可以迭代的对象-实际流程完善版

import time
from collections import Iterable
from collections import Iterator


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

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

    def __iter__(self):
        # return 一个对象的引用,同时这个对象内部必须含有__iter__()方法和__next__()方法
        return ClassIterator(self)  # 类名加个括号,表示创建了一个实例对象,相当于把这个实例对象的引用返回


class ClassIterator(object):
    """通过这个类创建出来的对象就可以作为__iter__()方法的返回值了"""
    def __init__(self, obj):  # obj指向了上一个类创建的对象
        self.obj = obj  # 意味着我找了一个属性,指向了上一个类创建出来的实例对象了
        self.i = 0

    def __iter__(self):
        pass

    def __next__(self):
        # 在这里实现将王五,刘五,孙五,每次返回一个
        # 如何在这个方法中够到要输出对象的列表?
        # 在上一个类中,已经创建了这个类的一个实例对象了,我想要用你的列表了,我只要能够到你的这个对象
        # 就能用你的列表了,怎样才能够到呢?
        # 在创建实例对象的时候如果可以写上上一个类的对象的引用(即把上一个类的self写进去):就是实现了
        # 把我自己的引用给了你一份,即你就可以在内存中找到列表所在的地址.
        # 上边传了一份引用过来(相当于传了一个实参过来,要在初始化方法中定义一个形参接收),
        # 一定要有一个方法来接收一下--__init__(self)方法

        # 如果想要同一个方法第二次第三次...调用的时候保留之前的值,循环初始值不能定义在方法内部
        # 而要定义在初始化方法内部(定义在初始化方法内部就相当于定义了一个实例属性,对象内部调用时要
        # 加self)
        if self.i < len(self.obj.names):
            ret = self.obj.names[self.i]
            self.i += 1
            return ret
        else:
            raise StopIteration
            # 数据输出结束以后,要想退出循环并结束可以抛出一个这样的异常
            # 若没有抛出,会由于没有返回值了,一直返回None


def main():
    """
        在真正的开发过程中,往往出现一种场景,我创建了一个对象,对象里边有个列表属性,列表内部放了
        我往里边添加的很多东西,那么我能不能用for循环的思路来去用列表里边的值呢?
            1.在创建这个对象的类里边必须实现__iter__()方法
            2.__iter__方法必须返回一个迭代器的对象
                    一个类中含有__iter__(self)方法和__next__(self)方法就可以
                    称为迭代器(可以取对象里边的值)

    """
    classmate = Classmate()
    classmate.add("王五")
    classmate.add("刘五")
    classmate.add("孙五")

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

if __name__ == "__main__":
     main()

04_自己实现一个可以迭代的对象-实际流程完善版简化版

import time
from collections import Iterable
from collections import Iterator


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

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

    def __iter__(self):
        # return 一个对象的引用,同时这个对象内部必须含有__iter__()方法和__next__()方法
        return self
        # return的返回值是一个迭代器,返回self是将自身作为迭代器

    def __next__(self):

        if self.i < len(self.names):
            ret = self.names[self.i]
            self.i += 1
            return ret
        else:
            raise StopIteration
            # 数据输出结束以后,要想退出循环并结束可以抛出一个这样的异常
            # 若没有抛出,会由于没有返回值了,一直返回None


def main():

    classmate = Classmate()
    classmate.add("王五")
    classmate.add("刘五")
    classmate.add("孙五")

    for name in classmate:  # 1.判断classmate是不是可以迭代的对象:找__iter__()方法
                            # 2.调用iter()函数:会自动调用__iter__()方法
        print(name)
        time.sleep(1)

if __name__ == "__main__":
     main()
 

05_fibonacci数列

# 方案一:找一个列表存储起来
nums = list()

a = 0
b = 1
i = 0
while i < 10:
    nums.append(a)
    a, b = b, a+b  # 等号右边相当于一个元组(1,0+1),将这个元组拆包成a,b两个里边的值
    i += 1

for num in nums:
    print(num)


06_fibonacci数列-2迭代器

import time
"""方案二:迭代器方案,全程没有出现列表,即产即传"""


class Fibonacci(object):
    def __init__(self,all_num):
        self.all_nums = all_num
        self.i = 0
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self  # 返回对象本身为迭代器

    def __next__(self):

        if self.i < self.all_nums:
            ret = self.a  # 设定一个变量为了将a = 0输出来
            self.a, self.b = self.b, self.a+self.b
            self.i += 1
            return ret
        else:
            raise StopIteration
            # 数据输出结束以后,要想退出循环并结束可以抛出一个这样的异常
            # 若没有抛出,会由于没有返回值了,一直返回None


def main():

    fibo = Fibonacci(10)

    for num in fibo:
        print(num)
        time.sleep(1)

if __name__ == "__main__":
     main()

07_自己改fibonacci数列-2迭代器

import time


class Nums(object):
    def __init__(self):
        self.nums = list()
        self.i = 0

    def add(self, a):
        self.nums.append(a)

    def __iter__(self):
        # return 一个对象的引用,同时这个对象内部必须含有__iter__()方法和__next__()方法
        return self
        # return的返回值是一个迭代器,返回self是将自身作为迭代器

    def __next__(self):

        if self.i < len(self.nums):
            ret = self.nums[self.i]
            self.i += 1
            return ret
        else:
            raise StopIteration
            # 数据输出结束以后,要想退出循环并结束可以抛出一个这样的异常
            # 若没有抛出,会由于没有返回值了,一直返回None


def main():

    nums = Nums()
    a = 0
    b = 1
    t = 0
    while t < 10:

        nums.add(a)
        a, b = b, a + b
        t += 1
    for num in nums:  # 1.判断classmate是不是可以迭代的对象:找__iter__()方法
                            # 2.调用iter()函数:会自动调用__iter__()方法
        print(num)
        time.sleep(1)

if __name__ == "__main__":
     main()

08_调试技巧-打印信息

def create_fibo(all_nums):
    """生成器的第二种创建方式"""
    print("*****1*****")
    # a = 0
    # b = 1
    a, b = 0, 1
    i = 0
    while i < all_nums:
        # print(a)
        print("*****2*****")
        yield a
        # 当函数执行到yield的时候,会暂停函数,将yield后边的值传递到for循环输出
        # 输出结束后会返回yield继续向下执行.而迭代器则是调用一次就执行一次next
        # 当函数中出现了 yield语句 这就不再是一个函数了,而变成了一个创建生成器的模版
        # 可以理解为跟类作为创建对象的模版一样的模版
        print("*****3*****")
        a, b = b, a+b
        i += 1
        print("*****4*****")

# 如果在调用creat_fibo()的时候,发现函数中含有yield,那么此时不是调用函数而是创建一个生成器对象
generator_obj = create_fibo(10)

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,故也可以使用next()调用生成器对象取里边的值
print(ret)

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,故也可以使用next()取里边的值
print(ret)



# 如何调用生成器,生成器是一种特殊的迭代器,就一定可以迭代,即可以借助for循环迭代输出
# for num in generator_obj:
    # print(num)

09_生成器研究

def create_fibo(all_nums):
    """生成器的第二种创建方式"""
    print("*****1*****")
    # a = 0
    # b = 1
    a, b = 0, 1
    i = 0
    while i < all_nums:
        # print(a)
        print("*****2*****")
        yield a
        # 当函数执行到yield的时候,会暂停函数,将yield后边的值传递到for循环输出
        # 输出结束后会返回yield继续向下执行.而迭代器则是调用一次就执行一次next
        # 当函数中出现了 yield语句 这就不再是一个函数了,而变成了一个创建生成器的模版
        # 可以理解为跟类作为创建对象的模版一样的模版
        print("*****3*****")
        a, b = b, a+b
        i += 1
        print("*****4*****")

# 如果在调用creat_fibo()的时候,发现函数中含有yield,那么此时不是调用函数而是创建一个生成器对象
generator_obj = create_fibo(10)
generator_obj_2 = create_fibo(10)

"""研究一:两个生成器之间的调用顺序没有影响"""

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,故也可以使用next()调用生成器对象取里边的值
print("generator_obj:", ret)

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,只要是迭代器就可以用next()启动,并获取值
print("generator_obj:", ret)

ret = next(generator_obj_2)  # 生成器是一种特殊的迭代器,故也可以使用next()取里边的值
print("generator_obj_2:", ret)

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,故也可以使用next()取里边的值
print("generator_obj:", ret)

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,故也可以使用next()取里边的值
print("generator_obj:", ret)

ret = next(generator_obj)  # 生成器是一种特殊的迭代器,故也可以使用next()取里边的值
print("generator_obj:", ret)

# 如何调用生成器,生成器是一种特殊的迭代器,就一定可以迭代,即可以借助for循环迭代输出
# for num in generator_obj:
    # print(num)

10_生成器研究2

def create_fibo(all_nums):
    # a = 0
    # b = 1
    a, b = 0, 1
    i = 0
    while i < all_nums:
        # print(a)
        yield a
        a, b = b, a+b
        i += 1
    return "***ok***"
    
generator_obj_2 = create_fibo(50)

"""研究二:异常"""

while True:
    try:
        ret = next(generator_obj_2)  # 生成器是一种特殊的迭代器,故也可以使用next()取里边的值
        print("generator_obj_2:", ret)
    except Exception as ret:
        print(ret.value)
        # 如果生成器是通过yield来做到的,要想得到generator_obj_2中return 返回的值
        # 当产生异常的时候,ret 中有个value属性内部就保存了return 返回的值,
        # 要想得到这个值,肯定是因为结束才告诉你的,结束后就捕获到了异常,ret就捕获到了这个
        # 异常对象,对象内部的value属性,就是return的结果

        break

11_使用生成器完成fibonacci数列

def create_fibo(all_nums):
    """生成器的第二种创建方式"""
    # a = 0
    # b = 1
    a, b = 0, 1
    i = 0
    while i < all_nums:
        # print(a)
        yield a
        # 当函数执行到yield的时候,会暂停函数,将yield后边的值传递到for循环输出
        # 输出结束后会返回yield继续向下执行.而迭代器则是调用一次就执行一次next
        # 当函数中出现了 yield语句 这就不再是一个函数了,而变成了一个创建生成器的模版
        # 可以理解为跟类作为创建对象的模版一样的模版
        a, b = b, a+b
        i += 1


# 如果在调用creat_fibo()的时候,发现函数中含有yield,那么此时不是调用函数而是创建一个生成器对象
generator_obj = create_fibo(10)
# 如何调用生成器,生成器是一种特殊的迭代器,就一定可以迭代,即可以借助for循环迭代输出
for num in generator_obj:
    print(num)

12_使用send来启动生成器

def create_fibo(all_nums):
    a, b = 0, 1
    i = 0
    while i < all_nums:
        ret = yield a
        print(ret)
        a, b = b, a+b
        i += 1

generator_obj = create_fibo(10)

ret = next(generator_obj)  # 先创建生成器对象,再把生成器对象放到next()内部
print(ret)

ret = generator_obj.send("5")  # 用生成器对象调用send(),send()内部传的值就是yield a 运行完的结论
print(ret)

13_使用yield完成多任务

import time
"""
并行:有两个任务,但现在我有4个cpu的核一个任务占一个cpu的核同时执行,这就叫做并行(真的多任务,同时完成的)
并发:有很多个任务,但是现在4个cpu的核(发动机,交替进行),这叫并发(假的多任务,短时间内交替完成的同一时刻只有一个在执行
下边程序交替进行,是个假的
协程实现多任务的最大特点:调用一个任务就像调用一个函数一样切换的资源最少,占用空间最小
"""


def tack_1():
    while True:
        print("*****1*****")
        time.sleep(0.1)
        yield  # next(t1)执行到yield先暂停,执行next(t2),next(t2)执行到yield先暂停,执行next(t1)


def tack_2():
    while True:
        print("*****2*****")
        time.sleep(0.1)
        yield


def main():
    t1 = tack_1()
    t2 = tack_2()
    while True:
        next(t1)
        next(t2)

if __name__ == "__main__":
    main()

14_使用greenlet完成多任务

from greenlet import greenlet
import time
"""yield可以实现多任务,greenlet包含它的功能却比它更加简好用"""

def test1():
    while True:
        print("---A--")
        gr2.switch()
        # 切换到gr2执行
        time.sleep(0.5)


def test2():
    while True:
        print("---B--")
        gr1.switch()
        time.sleep(0.5)
# gr1 gr2是全局变量,可以在各个函数中使用
gr1 = greenlet(test1)
# greenlet 是一个类,它的返回值是一个对象,相当于创建了一个对象,
# 类似于生成器对象,但他的内部进行了封装,也就是说greenlet这个类中对yield进行了封装,
# 使之更加高端,直接书写普通函数即可,不需要在写yield了
gr2 = greenlet(test2)

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

15_使用gevent完成多任务

import gevent
import time
"""
greenlet对yield进行了封装,gevent对greenlet进行了再次封装,所以说gevent相当于集合了前两者的全部功能,
在实现多任务的时候最方便实用的就是使用gevent

用多协程实现多任务的一种方式是利用了你原来在等待耗时的这个操作期间,腾出这个时间,去做其他的事情,这就是协程最核心的点
"""
"""
程序运行起来称之为进程,进程是资源分配的单位,线程负责执行代码,一个线程只能做一件事情,多线程可以实现多任务
若只有单线程,通过利用当一个任务在浪费时间的时候,把这个时间利用起来去做其他的事情,实现多任务

协程依赖于线程,线程依赖于进程
"""



def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        # gevent.getcurrent(),表示当前是哪个对象调用的这个函数
        # time.sleep(0.5)  # 可以产生耗时,但不切换任务,gevent内有自己的耗时等待语句
        gevent.sleep(1)


def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(1)


def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(1)

# 创建任务(产卵,量产),但不执行,(过程中不出现任何耗时语句的执行)
print("*****1*****")  # 使用打印的方法判断执行顺序
g1 = gevent.spawn(f1, 5)
# spawn()内部分别说明了,要到哪里去执行,以及是否需要传递参数
print("*****2*****")
g2 = gevent.spawn(f2, 5)
print("*****3*****")
g3 = gevent.spawn(f3, 5)
print("*****4*****")
g1.join()
# 等待,出现耗时
# 等待g1执行完,遇到延时操作自动切换任务
g2.join()
g3.join()

17_gevent打补丁

import gevent
import time
from gevent import monkey

monkey.patch_all()
# monkey 这句代码在调用的时候,会把当前整个代码读到某个地方去,自己检查所有出现耗时的地方(sleeo,accept,receive),
# 如果检查到不是gevent类型的,会自己把他换成gevent
# 这句代码保证了你原来的代码都不需要修改,会自己进行修改(防止里边有成千上万个要改)


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


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


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

# 创建任务(产卵,量产),但不执行,(过程中不出现任何耗时语句的执行)
print("*****1*****")  # 使用打印的方法判断执行顺序
g1 = gevent.spawn(f1, 5)
# spawn()内部分别说明了,要到哪里去执行,以及是否需要传递参数
print("*****2*****")
g2 = gevent.spawn(f2, 5)
print("*****3*****")
g3 = gevent.spawn(f3, 5)
print("*****4*****")
g1.join()
# 等待,出现耗时
# 等待g1执行完,遇到延时操作自动切换任务
# 由于有一个函数就要调用一次十分的麻烦,如果数量比较多的话使用gevent.joinall()比较好
g2.join()
g3.join()

gevent.joinall([
gevent.spawn(f1, 5),
gevent.spawn(f2, 5),
gevent.spawn(f3, 5)
        ])
# 将gevent创建出来的对象放到一个列表中,放进去即可,不用再单独定义变量调用

18_gevent的最终使用形式

import gevent
import time
from gevent import monkey
# 以后若想用协程做事情就直接使用gevent,不要考虑yield和greenlet这两个只是辅助理解gevent的

# 有耗时操作使需要
monkey.patch_all()
# 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块(打补丁)


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


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


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

gevent.joinall([
gevent.spawn(f1, 5),
gevent.spawn(f2, 5),
gevent.spawn(f3, 5)
        ])

19_爬虫-图片下载器

import urllib.request
import gevent


def downloder(img_name,img_url):
    req = urllib.request.urlopen(img_url)
    img_content = req.read()
    with open(img_name, "wb") as f:
        f.write(img_content)

def main():
    gevent.joinall([
        gevent.spawn(downloder, "1.jpg",'http://g.hiphotos.baidu.com/image/h%3D300/sign=342e12b86563f624035d3f03b745eb32/203fb80e7bec54e7f0e0839fb7389b504fc26a27.jpg'),
        gevent.spawn(downloder, "2.jpg",'http://f.hiphotos.baidu.com/image/h%3D300/sign=705ffa145fda81cb51e685cd6267d0a4/4bed2e738bd4b31c5a30865b89d6277f9f2ff8c6.jpg')
    ])
if __name__ == '__main__':
    main()

发布了21 篇原创文章 · 获赞 2 · 访问量 826

猜你喜欢

转载自blog.csdn.net/m0_44967199/article/details/95376106
今日推荐