zoukankan      html  css  js  c++  java
  • Python多线程和多进程

    线程是一个操作系统级别的概念,编程语言本身不创建线程,而是调用操作系统层提供的接口创建、控制、销毁线程实例。
    他们所支持的线程底层实现和操作效果也是不尽相同的。不过一个操作系统支持的线程至少会有四种状态:就绪、执行、阻塞和终结。线程在四种状态下进行切换,都是要消耗不少的CPU计算能力的。
    并且根据操作系统使用线程的进程的不一样,线程还分为用户线程和操作系统线程。操作系统线程(内核线程),是指操作系统内核为了完成硬件接口层操作,由操作系统内核创建的线程:例如I/O操作的内核线程,这些线程应用程序是不能干预的;用户线程,是指用户安装/管理的应用程序,为执行某一种操作,而由这个应用程序创建的线程。
    线程在创建时,操作系统不会为这个线程分配独立的资源(除了必要的数据支撑)。一个应用程序(进程)下的所有线程,都是共享这个应用程序(进程)中的资源,例如这个应用程序的CPU资源、I/O资源、内存资源。

    多线程是一门编程语言的重要操作。

    GIL(全局解释器锁)存在于python解释器中,用来确保当前只有一个线程被执行,当一个线程获得GIL后,这个线程将被执行,在python2中,执行了一定数量的字节码,或者遇到IO操作会退出释放GIL,在python3.2以后,使用了新的GIL,新的GIL实现中用一个固定的超时时间来指示当前的线程放弃全局锁。在当前线程保持这个锁,且其他线程请求这个锁时,当前线程就会在5毫秒后被强制释放该锁。由下一个获得GIL的线程执行,这导致了纯Python代码使用多线程并不能提高运行速率,只是在遇到需要等待操作的时候,使用多线程会提升效率。

    python多线程有两个模块,threading和thread,一般情况下使用threading,特殊情况才使用thread,下面是原因:

    1. threading模块对同步原语的支持更为完善和丰富。
    2. threading模块在主线程和子线程交互上更为友好。
    3. thread模块不支持守护线程。
      thread模块中主线程退出的时候。所有的子线程不论是否还在工作,都会被强制结束。
    4. python3中已经不存在thread模块。

    Python多线程支持两种方式来创建线程:一种是继承Thread类,重写他的run()方法,另一种是创建一个threading.Thread对象,在它的初始化函数中(init())中可将调用对象作为参数传输。

    常用Threading模块的方法:

    • threading.Thread(self, group=None, target=None, name=None,
      args=(), kwargs=None, verbose=None))
    target: 指定线程由 run () 方法调用的可调用对象。默认为 None, 意味着不调用任何内容。
    name: 指定该线程的名称。 在默认情况下,创建一个唯一的名称。
    args: target调用的实参,元组格式。默认为 (),即不传参。
    daemon: 为False表示父线程在运行结束时需要等待子线程结束才能结束程序,为True则表示父线程在运行结束时,子线程无论是否还有任务未完成都会跟随父进程退出,结束程序。(python2和3可能有区别)
    
    • start方法和run方法
    start() 方法是启动一个子线程,线程名就是我们定义的name
    run() 方法并不启动一个新线程,就是在主线程中调用了一个普通函数而已。
    想启动多线程,就必须使用start()方法。
    
    • threading.Timer(interval, function, args=[], kwargs={})

    threading的派生类,可以用来定时任务,如果要重复执行某个任务,可以在function中再定义一个threading.time()

    • threading.current_thread().name (或者threading.current_thread().getName())

    线程名,只是一个标识符,可以使用getName()、setName()获取和运行时重命名。

    • threading.current_thread().ident

    线程ID,非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。此ID可以重复使用

    • threading.current_thread().is_alive()

    返回线程是否存活,布尔值,True或False。

    • threading.active_count()

    返回当前活跃的Thread对象数量。返回值和通过enumerate()返回的列表长度是相等的。

    • join([timeout])方法
    threading.Thread.join。主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
    里面的参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的。
    join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞,设置为None则等待子进程执行完毕
    
    
    • setDaemon()方法

    主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。

    在CPU密集型任务下,多进程更快,或者说效果更好;而IO密集型,多线程能有效提高效率。

    python的两个多进程库
    subprocess和multiprocess
    区别:
    subprocess:
    subprocess 用来执行其他的可执行程序的,即执行外部命令。 他是os.fork() 和 os.execve() 的封装。 他启动的进程不会把父进程的模块加载一遍。使用subprocess的通信机制比较少,通过管道或者信号机制.
    由于在windows中没有os.fork,所以创建进程采用的是createprocess()
    Linux的进程创建是通过系统调用fork()

    multiprocessing:
    multiprocessing 用来执行python的函数,他启动的进程会重新加载父进程的代码。可以通过Queue、Array、Value等对象来通信。

    subprocess 用来执行外部命令,是os.fork() 和 os.execve() 的封装,即先fork一个子进程,再运行新的外部程序,子进程不会把父进程的模块加载一遍;
    而multiprocessing的原理是fork,fork()调用:调用1次,返回两次--操作系统自动把当前进程(父进程)复制了一份(子进程),然后,分别在父进程和子进程内返回,父进程返回子进程的pid,子进程返回0,即父进程和子进程都在运行。
    对于外部调用来说,使用multiprocessing太占资源。
    进程之间是数据隔离的,想通讯要用特殊的方式。

    在windows中必须把Process()放到if name == "main"语句下

    由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。
    如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。
    这是隐藏对Process()内部调用的原,使用if name == “__main __”,这个if语句中的语句将不会在导入时被调用。
    由于Python运行过程中,multiprocess新创建进程后,进程会导入正在运行的文件,即在运行代码0.1的时候,代码在运行到mp.Process时,新的进程会重新读入改代码,对于没有if name=="main"保护的代码,新进程都认为是要再次运行的代码,这是子进程又一次运行mp.Process,但是在multiprocessing.Process的源码中是对子进程再次产生子进程是做了限制的,是不允许的,于是出现如上的错误提示。

    进程之间的通讯可以通过Value和Array进行,Value是单个变量,Array是数组
    from multiprocess import Value,Array
    num = Value('d', 0.0)
    arr = Array('i', range(10))
    创建num和arr时,“d”和“i”参数由Array模块使用的typecodes创建:“d”表示一个双精度的浮点数,“i”表示一个有符号的整数,这些共享对象将被线程安全的处理。小写是有符号,大写是无符号。
    print(num.value)
    print(arr[0])
    ‘c’: ctypes.c_char     ‘u’: ctypes.c_wchar    ‘b’: ctypes.c_byte     ‘B’: ctypes.c_ubyte
    ‘h’: ctypes.c_short    ‘H’: ctypes.c_ushort   ‘i’: ctypes.c_int      ‘I’: ctypes.c_uint
    ‘l’: ctypes.c_long,    ‘L’: ctypes.c_ulong    ‘f’: ctypes.c_float    ‘d’: ctypes.c_double

  • 相关阅读:
    PAT Basic 1023 组个最小数 (20 分)
    PAT Advanced 1048 Find Coins (25 分)
    PAT Basic 1005 继续(3n+1)猜想 (25 分)
    PAT Advanced 1050 String Subtraction (20 分)
    PAT Advanced 1041 Be Unique (20 分)
    PAT Basic 1047 编程团体赛 (20 分)
    SpringSecurity整合SpringBoot
    给正在运行的Docker容器动态绑定卷组
    linux 中格式化json字符串
    docker/kubernetes国内源/镜像源解决方式
  • 原文地址:https://www.cnblogs.com/MJ-CAT/p/10786420.html
Copyright © 2011-2022 走看看