5.Python多进程与多线程

    线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
    多进程和多线程的程序涉及到同步、数据共享的问题,编写起来更复杂。
    
    在Unix/Linux下,可以使用fork()调用实现多进程。
    要实现跨平台的多进程,可以使用multiprocessing模块。
    进程间通信是通过Queue、Pipes等实现的
    
    多任务可以由多进程完成,也可以由一个进程内的多线程完成。
    启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行
    任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,
    Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定
    
    多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,
    而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,
    因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
    由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现
    当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
    获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放
    多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。
    Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦
    
    在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。
    一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题
    ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
    
    对比:
    多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。(当然主进程挂了所有进程就全挂了,但是Master进程只负责分配任务,挂掉的概率低)
    多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大。
    另外,操作系统能同时运行的进程数也是有限的,在内存和CPU的限制下,如果有几千个进程同时运行,操作系统连调度都会成问题。
    多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。
    在Windows上,如果一个线程执行的代码出了问题,你经常可以看到这样的提示:“该程序执行了非法操作,即将关闭”,其实往往是某个线程出了问题,但是操作系统会强制结束整个进程。
    在Windows下,多线程的效率比多进程要高
    
    我们前面编写的所有的Python程序,都是执行单任务的进程,也就是只有一个线程。如果我们要同时执行多个任务怎么办?
    有两种解决方案:
    
    一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。
    还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。
    当然还有第三种方法,就是启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型更复杂,实际很少采用。

    总结一下就是,多任务的实现有3种方式:

    多进程模式;
    多线程模式;
    多进程+多线程模式。
    
    
    区别:
    1.同一个进程中的线程共享同一内存空间,但是进程之间是独立的。
    2.同一个进程中的所有线程的数据是共享的(进程通讯),进程之间的数据是独立的。
    3.对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。
    4.线程是一个上下文的执行指令,而进程则是与运算相关的一簇资源。
    5.同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理来实现。
    6.创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。
    7.一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。
    8.线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。

猜你喜欢

转载自blog.csdn.net/Wgb_0206/article/details/88620433