zoukankan      html  css  js  c++  java
  • Python学习之路-随笔03 多线程/进程和协程(上篇)

      

     最近东西积攒了太多,感觉再不写进来就要炸了。

    1.多线程

    1.11 关于多线程的包

    相关的python包有几个,比如thread包,到py3改成_thread,而thread有一些问题使得不是很好用。通用的包叫threading。最近都是在用这个。

    1.12 threading的使用和常用属性

    需要注意的点有生成实例比如t = threading.Thread(target=xxx, args=(xx,)),里面有两个参数,第一个是目标函数,第二个是相关的参数,注意类型。

    然后就是start启动,join等待多线程实行完成这两个方法。

    #伪代码
    t1 = threading.Thread(target=fun1, args=("函数参数 1",))
    t1.start()
    
    t2 = threading.Thread(target=fun2, args=("函数参数1", "函数参数2"))
    t2.start()
    
    #等待线程
    t1.join()
    t2.join()

    继承threading.Thread使用,需要重写run函数。

    class MyThread(threading.Thread):
      def __init__(self, arg):
        super(MyThread, self).__init__()
        self.arg = arg

      def run(self):
        time.sleep(2)

    其他常用属性和方法有:

    threading.currentThread:返回当前线程变量

    threading.enumerate:返回一个包含正在运行的线程的list,正在运行的线程指的是线程启动后,结束前的状态

    threading.activeCount: 返回正在运行的线程数量,效果跟 len(threading.enumerate)相同

    threading.timer: 定时器,利用多线程在指定时间后启动一个功能

    thr.setName: 给线程设置名字

    thr.getName: 得到线程的名字

    守护线程:daemon:即如果将子线程设置成守护线程,则子线程会在主线程结束的时候自动退出,一般认为,守护线程不中要或者不允许离开主线程独立运行

    而守护线程能否有效果跟环境相关

    t1 = threading.Thread(target=fun, args=() )
    # 社会守护线程的方法,必须在start之前设置,否则无效
    t1.setDaemon(True)
    #t1.daemon = True
    t1.start()

    1.13 线程相关的问题

    因为线程之间有共享状态(资源),会有一些诸如死锁或者同步的问题。解决方法也是大同小异,这些python里都给出了

    相应的工具。比如自己设置一个锁/信号灯。用semphore变量,还有可重入锁解锁递归的时候申请锁的问题等等。

    线程安全不安全变量list,set,dict,安全变量queue等。

    1.14 线程替代方案

    替代无非是用进程或者魔改的线程包,诸如subprocess使用进程,multiprocessing用threading派生出来的,使用子进程,可以使用多核或多CPU

    还有一个current.future待会儿写

    2.多进程

    使用的就是上面提到的线程替代方案multiprocessing,方法和threading差不多,multiprocessing.Process(target=xxx, args=(xx,)),然后同样也可以继承后使用,实现方式类似多线程

    还有一个就是pid和ppid,即进程ID和父进程ID,还有其他常见的内容比如子进程和父进程共享资源啊,而子线程有自己独立的栈空间啊之类的就不详细描述了

    还有其他的诸如生产者-消费者模型,读者-写者模型等等。和上面的一样,也不赘述了,操作系统书里都有,写在这没什么意义。

    3.协程

    首先来写一下协程的定义:协程是为非抢占式多任务产生子程序的计算机程序组件, 协程允许不同入口点在不同位置暂停或开始执行程序。 

     在这之前先写两个

    3.11 迭代器

    有两个概念,一是可迭代对象Iterable,二是迭代器Iterator

    Iterable可以用于for循环,二Iterator不仅可以用于for循环还能被next函数调用,可以用isinstance函数判断,比如list是可迭代的。

    from collections import Iterable
    
    l = [1,2,3,4]
    
    isinstance(l, Iterable)

    out:True

    两者之间还可以通过iter函数转换

    from collections import Iterator
    
    isinstance(iter('abc'), Iterator)

    out:True

    3.12 生成器

    比如最常见的生成器有L = [x * x for x in range(10)],g = (x * x for x in range(10))这些生成列表,元组的方法

    本质是一个函数/方法,每次调用next的时候计算下一个值,最后会抛出StopIteration异常。

    函数中包含yield,则叫generator

    next调用,遇到yield返回

    # 在函数odd中,yield负责返回
    def odd():
        print("Step 1")
        yield 1
        print("Step 2")
        yield 2
        print("Step 3")
        yield 3
     
    # odd() 是调用生成器
    g = odd()
    one = next(g)
    print(one)
    
    two = next(g)
    print(two)
    
    three = next(g)
    print(three)

    out:Step 1

      1

      Step 2

      2

      Step 3

      3

    3.13 协程

    是的,协程和生成器很像,使用yield和send。目的是合理调用各种系统资源和进行协同工作。协程之间切换耗费也很低

    协程有四个状态:

    GEN_CREATED:等待开始执行

    GEN_RUNNING:解释器正在执行

    GEN_SUSPENED:在yield表达式处暂停

    GEN_CLOSED:执行结束

    def simple_coroutine(a):
        print('-> start') 
    
        b = yield a # 把A丢出来给aa
        print('-> recived', a, b)
    
        c = yield a + b
        print('-> recived', a, b, c)
    
    # runc
    sc = simple_coroutine(5)
    
    aa = next(sc) # 预激,准备好执行协程,程序走到yield停下
    print(aa) 
    bb = sc.send(6) # 5 ,6  把参数6发给b
    print(bb)
    cc = sc.send(7) # 5, 6, 7
    print(cc)#执行不到,抛出StopIteration异常

    out:

    -> start

    5

    -> recived 5 6

    11

    -> recived 5 6 7

    之前说过,最后会抛出一个StopIteration异常,未处理的异常会向上冒泡,传给next函数或send函数的调用方。终止协程可以发送某个哨符,让协程退出,比如内置的None和Ellipsis等常量

    还有两个和异常相关的方法generator.throw(Exctpiton)和generator.close()

    大概作用就是前一个会在暂停的yield出抛出指定的异常。如果生成器处理了该异常,则代码会执行到下一个yield

    而产出的值(value,异常的一个属性)会成为调用 generator.throw方法得到的返回值。没处理则往上冒泡。

    第二个的作用是致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果生成器没有处理这个异常,或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错。

    如果收到 GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出RuntimeError 异常。生成器抛出的其他异常会向上冒泡,传给调用方。

    以下是找到的一段事例代码

    class DemoException(Exception):
        """
        custom exception
        """
        pass
    
    def handle_exception():
        print('-> start')
    
        while True:
            try:
                x = yield
            except DemoException:
                print('-> run demo exception')
            else:
                print('-> recived x:', x)
    
        raise RuntimeError('this line should never run')
    
    he = handle_exception()
    next(he)
    he.send(10) # recived x: 10
    he.send(20) # recived x: 20
    
    he.throw(DemoException) # run demo exception
    
    he.send(40) # recived x: 40
    he.close()
    View Code

    yield from

    相对于yield,yield from就相当于在调用方和生成器之间多了一层“通道”类似的代理。(又或者说在主线程和协程之间)

    使用的渠道:比如委派生成器。

    1,定义一个生成器(含yield)可以一次次的接收调用方传来的值(通过yield)和return回去处理后的值

    2,定义个委派生成器,只需要yield from 生成器即可使用并接收值

    3,调用委派生成器使用

    未完待续

  • 相关阅读:
    Daliy Algorithm (dp,模拟)-- day 80
    Daliy Algorithm (dp,堆)-- day 79
    Mybatis一级缓存和二级缓存 Redis缓存
    简单排序
    java一个大接口拆用多线程方式拆分成多个小接口
    集群环境下Shiro Session的管理
    递归和快速排序
    分布式定时任务
    Redis集群架构
    IO流
  • 原文地址:https://www.cnblogs.com/slose-notes/p/9672312.html
Copyright © 2011-2022 走看看