Python学习理解进程与线程

╄→尐↘猪︶ㄣ 提交于 2020-02-01 09:00:55

1.进程与线程基本概念

进程(英语:process),是指计算机中已运行的程序。进程曾经是分时系统的基本运作单位。在面向进程设计的系统(如早期的UNIX,Linux 2.4及更早的版本)中,进程是程序的基本执行实体;在面向线程设计的系统(如当代多数操作系统、Linux 2.6及更新的版本)中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或异步(平行)的方式独立运行。现代计算机系统可在同一段时间内以进程的形式将多个程序加载到存储器中,并借由时间共享(或称时分复用),以在一个处理器上表现出同时(平行性)运行的感觉。同样的,使用多线程技术(多线程即每一个线程都代表一个进程内的一个独立执行上下文)的操作系统或计算机体系结构,同样程序的平行线程,可在多CPU主机或网络上真正同时运行(在不同的CPU上)。来源维基百科

线程(英语:thread),是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。来源维基百科

2.总结一下进程与线程的关系以及区别

  • 进程与线程的关系
    (1) 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
    (2) 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
    (3) 处理机分给线程,即真正在处理机上运行的是线程。
    (4) 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是进程内的一个执行单元,也是进程内的可调度实体。
  • 进程与线程的区别
    (1) 调度:线程作为调度和分配的基本单元,进程作为拥有资源的基本单位。
    (2) 并发性:不仅进程之间可以并发执行,同一进程的多个线程之间也可以并发执行。
    (3) 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
    (4) 系统开销:在创建或撤销进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤销线程时的开销。

3.Python多进程创建方式
可以归纳为三种:fork,multiprocessing以及进程池Pool。
(1) fork方式
注意:fork()函数只能在Unix/Linux/Mac上面运行,不能在Windows上面运行。
说明:

  • 程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中。
  • 然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的id号。
import os

# 注意fork函数,只在Unix/linux/Mac上运行
pid = os.fork()

if pid == 0:
    print('haha1')
else:
    print('haha2')

(2) multiprocessing方式
由于Windows没有fork调用,那么在Windows上无法使用Python编写多进程程序了吗?答案当然是NO!由于Python是跨平台的,自然有支持跨平台的多进程模块,而multiprocessing模块既支持跨平台的多进程。

import os
import time
from multiprocessing import Process

def run_proc(name):
    print('子进程运行中,name%s,pin=%d...' %(name,os.getpid()))
    time.sleep(10)
    print('子进程已经结束')

if __name__=='__main__':
    print('父进程%d.' %os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将要执行')
    p.start()
/home/jmrh/anaconda3/envs/pytorch/bin/python /home/jmrh/PycharmProjects/Project/Thread.py
父进程29484.
子进程将要执行
子进程运行中,nametest,pin=29485...
子进程已经结束

Process finished with exit code 0

从结果中可以看出,只有通过start()开启了子进程之后,主进程会等待子进程执行完才结束。
(3) Pool方式

from multiprocessing import Pool

def worker(msg):
    print("%s开始执行,进程号%d" %(msg, os.getpid()))
    time.sleep(1)
    print("%s执行完毕"%(msg))
if __name__=='__main__':
    po = Pool(3)  # 定义一个进程池,最大进程数3
    for i in range(10):
        # Pool.apply_async(要调用的目标,(传递给目标的参数元组,))
        # 每次循环将会用空闲出来的子进程去调用目标
        po.apply_async(worker, (i,))

    print("-----start-----")
    po.close()   # 关闭进程池,关闭后po不再接受新的请求
    po.join()    # 等待po中所有子进程执行完成,必须放在close语句之后
    print("-----end-----")

4.Python中的线程
Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数下,我们只需要使用threading这个高级模块。

import threading
import time

def job():
     print("这是一个需要执行的任务.")
     print("当前线程的个数:",threading.active_count())
     print("当前线程的信息:",threading.current_thread())
     time.sleep(10)
 if __name__== '__main__'
     job()

/home/jmrh/anaconda3/envs/pytorch/bin/python /home/jmrh/PycharmProjects/Project/Thread.py
这是一个需要执行的任务
当前线程的个数: 1
当前线程的信息: <_MainThread(MainThread, started 140515823023872)>

线程的生命周期图

在这里插入图片描述
线程交互执行

  • join()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用现成的join方法。
  • setDaemon()方法:主线程A中,创建了子线程 B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出,这就是setDaemon方法的含义,这基本和join是相反的。此外,需特别注意的:必须在start()方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。

5.Python中的锁
死锁:指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

  • 全局解释器锁(GCL)
  • 同步锁
  • 递归锁和死锁
  • 信号量(semaphore)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!