进程 线程

进程

多任务

程序的运行都是cpu和内存协同工作的结果
常用的操作系统都支持“多任务”的操作
问题1:什么时多任务
  操作系统上同时运行多个任务,除了手动打开应用程序之外,还有很多的后台程序
问题2:单核cpu是怎么执行多任务
  操作系统会让多个任务轮流交替执行,由于不同的任务之间的切换速度很快,导致肉眼无法识别,
  多核cpu实现多任务:真正的并行执行之恶能在多核cpu上执行,但是现实情况下,任务的数量可能会远远大于cpu的核心数量,操作系统会将每个任务调度,让每个cpu核心上轮流执行
  并行:真正一起执行,任务的数量小于cpu的核心数量[理想型]
  并发:看上去一起执行,任务数量大于cpu的核心数量[现实型]
问题3:什么是进程?
  对于操作系统而言,一个任务就是一个进程[Process]
  例如:打开一个浏览器启动了一个浏览器进程,打开一个Word启动了一个word进程,但是,一个进程可以同时干多件事情,在一个进程的内部,要同时干多个事情,就需要同时执行多个子任务,将一个进程中的子任务被称为线程[Thread]

进程本质

是对一个程序的运行状态和占用资源[cpu,内存]的描述
进程是程序的一个运动过程,他指的是从代码到加载到最后代码加载完毕的整个过程
进程的特点:
  a.独立性:不同的进程之间是相互独立的,在操作系统中互不影响
  b.动态性:进程一旦被启动之后,在操作系统中并不是静止不动的,一直处于动态状态
  c.并发性:在操作系统上交替执行

python多任务实现方式

a.多进程模式:启动多个进程,每个进程虽然只有一个线程,通过该线程执行一个任务
  b.多线程模式:启动一个进程,该进程启动多个线程,每个线程执行一个任务
  c.协程模式:yield[函数生成器]
  d:多进程模式+多线程模式:启动多个进程,每个进程启动多个线程,执行的任务的数量会更多

使用

单任务现象

import  time

#单任务:一个函数执行了某个特定的功能
def run():
    while True:
        print("hello")
        time.sleep(0.5)

if __name__ == "__main__":
    while True:
        print("main")
        time.sleep(1)

    run()

#说明:单任务现象代码从上往下依次执行,前面的代码执行完毕,后面的代码才有执行的机会

启动多进程实现多任务


import  time,os
#如果要实现多任务,Python中提供了相应的模块
#每通过Process创建一个对象,则表示创建一个进程
from  multiprocessing import  Process

#子进程中的任务
def run(s):
    #os.getpid()获取当前正在执行的进程id
    #os.getppid(获取当前正在执行进程的父进程id
    print("子进程启动:%s-%s" % (os.getpid(),os.getppid()))
    print(s)
    while True:
        print("hello")
        time.sleep(1)

if __name__ == "__main__":
    #1.__main__中执行的代码表示主进程,也称为父进程
    #os.getpid获取当前正在执行的进程id
    print("主进程启动:%s" % (os.getpid()))

    #2.在主进程中创建子进程
    #Process(target,args),target表示子进程需要执行的任务【函数】,args表示需要执行的任务的参数,类型为元组
    #2.1任务函数没有参数
    #p1 = Process(target=run)

    #2.2任务函数有参数
    #注意:给子进程中的任务传参的时候,函数有几个参数,元组中的元素需要保持一致
    p1 = Process(target=run,args=("text",))

    #如果想要启动一个子进程,则必须调用start
    p1.start()

    #主进程中的任务
    while True:
        print("main")
        time.sleep(1)

    run()

父子进程的执行顺序

from  multiprocessing import  Process
import  time,os

def run():
    print("子进程启动~~~~~")
    time.sleep(2)
    print("子进程结束~~~~~")


if __name__ == "__main__":
    print("父进程启动")
    time.sleep(2)

    p = Process(target=run)

    p.start()

    p.join()

    print("父进程结束")

"""
默认情况下,都是主进程结束之后子进程才启动
使用join之后,主进程会等待子进程结束之后才结束

注意:join的调用只能在start之后
"""

多个进程中的全局变量

from  multiprocessing import  Process
from time import  sleep
import  os

#需求:在一个进程中修改全局变量的值,观察另一个进程中全局变量的值的变化

#全局变量
num = 100

def run():
    print("子进程开始:%s" % (os.getpid()))
    global  num
    num += 1
    print("子进程结束:num=%d" % (num))

if __name__ == "__main__":
    print("父进程开始:%s" % (os.getpid()))

    p = Process(target=run)
    p.start()

    p.join()

    print("父进程结束:num=%d" % (num))

"""
说明:在子进程中修改全局变量对父进程中的全局变量没有影响
工作原理;在创建子进程的时候,实际上对全局变量做了一个备份,父进程中的num和子进程中的num是两个不同的变量
进程是系统中程序执行和系统资源分配的基本单位,每个进程都有自己的数据段,代码段,堆栈段
【进程之间是相互独立的,资源不共享】
"""

验证多个进程之间的独立性

启动大量子进程

如果要大量创建子进程,可以使用进程池的方式批量创建子进程

#进程池
from  multiprocessing import  Pool
import  os,time,random

#需求:创建多个子进程,让子进程执行同一个任务,根据传入的参数作为进程的名称进行区分

def run(name):
    print("子进程%s启动:%s" % (name,os.getpid()))

    #通过time计算耗时的时间差
    start = time.time()
    time.sleep(random.choice(range(1,4)))
    end = time.time()

    print("子进程%s结束,进程号:%s,耗时:%s" % (name, os.getpid(),end - start))

if __name__ == "__main__":
    print("父进程启动")

    #创建多个进程,采用进程池的方式,通过系统类Pool创建多个进程对象
    #Pool(num),num表示需要同时执行的任务的数量,可以省略,默认为cpu的核心数量
    p = Pool(8)

    #通过循环的方式创建多个子进程
    for i in range(5):
        #创建子进程,放到进程池统一管理
        #apply_async(target,args)
        p.apply_async(run,args=(i,))

    #必须在join之前调用close,调用close之后将不能再添加新的进程
    p.close()

    #进程池对象调用join,触发进程的启动,父进程会等待所有的子进程执行结束之后才结束
    p.join()

    print("父进程结束")

"""
如果要使用进程池创建多个进程,必须调用close和join
"""
#需求:实现文件内容的拷贝

import  os,time

#实现文件的拷贝
def copyfile(rpath,wpath):
    fr = open(rpath,"rb")
    fw = open(wpath,"wb")

    content = fr.read()
    fw.write(content)

    fr.close()
    fw.close()

if __name__ == "__main__":
    path = r"C:\Users\Administrator\Desktop\text1"
    topath = r"C:\Users\Administrator\Desktop\text2"

    #获取path下面的所有的内容
    filelist = os.listdir(path)

    start = time.time()

    for filename in filelist:
        copyfile(os.path.join(path,filename),os.path.join(topath,filename))

    end = time.time()

    print("总耗时:%.2f" % (end - start))
# 需求:实现文件内容的拷贝

import os, time
from  multiprocessing import  Pool


# 实现文件的拷贝
def copyfile(rpath, wpath):
    fr = open(rpath, "rb")
    fw = open(wpath, "wb")

    content = fr.read()
    fw.write(content)

    fr.close()
    fw.close()


if __name__ == "__main__":
    path = r"C:\Users\Administrator\Desktop\text1"
    topath = r"C:\Users\Administrator\Desktop\text3"

    # 获取path下面的所有的内容
    filelist = os.listdir(path)

    p = Pool(4)

    start = time.time()

    for filename in filelist:
        #copyfile(os.path.join(path, filename), os.path.join(topath, filename))
        p.apply_async(copyfile,args=(os.path.join(path, filename),os.path.join(topath, filename)))

    p.close()
    p.join()

    end = time.time()

    print("总耗时:%.2f" % (end - start))

封装进程对象

from  multiprocessing import  Process

import  os,time

#1.自定义一个类,继承自Process
class CustomProcess(Process):
    #2.书写构造函数,定义实例属性,表示当前进程的名称
    def __init__(self,name):
        #3.调用服了你的构造函数
        Process.__init__(self)
        self.name = name

    #4.定义成员函数,任务的执行函数
    def run(self):
        print("子进程(%s-%s)启动" % (self.name,os.getpid()))
        #子进程的功能
        time.sleep(2)
        print("子进程(%s-%s)结束" % (self.name, os.getpid()))

if __name__ == "__main__":
     print("父进程启动")

     p = CustomProcess("1111")
     p.start()

     p.join()

     print("父进程结束")

进程之间的通信

Process之间是需要相互通信的,操作系统提供了很多的办法实现进程间的通信,Python中的multiprocessing包装了底层的机制,提供了Queue[队列],

#需求:在主进程中创建两个子进程,一个进程用来向队列中写数据,另外一个进程用来从队列中读数据

from  multiprocessing import  Process,Queue
import  os,time,random

#1.写数据的任务
def write(queue):
    print("进程%s开始" % (os.getpid()))
    print("开始写入")

    for value in ["A","B","C"]:
        print("add %s to queue" % (value))

        #向队列中添加数据
        queue.put(value)

        #稍休息片刻,接着加
        time.sleep(random.random())

    print("进程%s结束" % (os.getpid()))

#2.读数据的任务
def read(queue):
    print("进程%s开始" % (os.getpid()))
    print("开始读取")

    n = 0
    while n < 3:
        #从队列中获取数据
        value = queue.get(True)
        print("get %s from queue" % (value))
        n += 1

    print("进程%s结束" % (os.getpid()))

#3.在主进程中分别创建子进程,执行相应的任务
if __name__ == "__main__":
    print("父进程启动")

    #3.1创建队列对象,并传参给每个进程
    q = Queue()

    #3.2创建进程对象,
    pr = Process(target=read,args=(q,))
    pw = Process(target=write,args=(q,))

    #3.3启动子进程,让进行数据的读写
    pw.start()
    pr.start()

    #3.4设置,让所有子进程结束之后,才结束父进程
    pr.join()
    pw.join()

    print("父进程结束")

二.线程

概念

是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务
注意:一个进程至少需要一个线程,否则该进程没有意义.
线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是在cpu之间进行快速的切换,[每个线程都有争抢时间片的机会,谁抢到时间片,则执行对应的线程,剩下的线程都会被挂起]

例如: 打开网易云音乐----->启动一个进程
   播放歌曲和刷新歌词------->启动了两个线程
进程和线程的关系
a.一个程序启动之后至少有一个进程
b.一个进程可以包含多个线程,但是至少需要一个线程,否则该进程没有意义
c.进程之间的资源不共享,线程之间的资源是共享的
d.系统创建进程需要为该进程重新分配系统资源,二创建线程容易的多,因此使用多线程实现多任务比多进程实现效率更高

创建线程

_thread模块,提供了低级别的,原始的线程[低级别并不是不好,知识功能有限,底层采用的c语言]
threading模块:高级模块,对_thread进行了封装,提供了_thread没有的功能

import  threading,time


#2.定义子线程需要执行的任务
def run(num):
    print("子线程%s启动" % (threading.current_thread().name))

    time.sleep(2)
    print("num=%d" % (num))

    print("子线程%s结束" % (threading.current_thread().name))

if __name__ == "__main__":
    #1.任何进程默认会启动一个线程,称为主线程,在主线程中启动其他的子线程
    #threading.current_thread()返回一个当前正在执行的线程对象
    #threading.current_thread().name获取当前正在执行的线程的名称,主线程默认的名称为MainThread
    print("主线程%s启动" % (threading.current_thread().name))

    #3.创建子线程
    #threading.Thread(target,name,args),target需要执行的任务,name表示线程的名称,args执行的任务函数的参数
    #3.1任务函数没有参数
    #注意:name也可以省略,每个子线程默认有名称,根据线程被创建的顺序,为Thread-1,Thread-2.....
    #t1 = threading.Thread(target=run,name="11111")

    #3.2任务函数有参数
    t1 = threading.Thread(target=run,args=(10,))

    #4.手动启动子线程
    t1.start()

    #5.想让主线程等待子线程执行结束之后才结束,设置join
    t1.join()

    print("主线程%s结束" % (threading.current_thread().name))

线程中的数据共享

问题:在多线程中,全局变量被所有线程所共享,所以,任何一个全局变量可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时修改同一个变量,容易将数据改乱

#需求:使用一个全局变量代表银行卡余额,两个线程都可以访问银行卡的余额

from  threading import  Thread

balance = 0

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子线程的任务
def run(n):
    for _ in range(1000000):
        change(n)

if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理论上,balance最终的结果为0
    print(balance)

"""
问题:
    先加后减,理论上结果为0,但是,如果访问的次数变多,则结果不一定为0

原因:
    使用多线程容易出现的临界资源问题
    高级语言中一条语句分步执行的,所以balance = balance + n在内存中执行的顺序是:
        a.计算balance + n,将结果存储到一个临时变量中   x = balance + n
        b.将临时变量的值赋值给balance   balance = x

    1.正常情况下
        t1:x = balance + n  #5
        t1:balance = x   #balance = 5
        t1:x = balance - n   #0
        t1:balance = x    #0

        t2:x = balance + n  #8
        t2:balance = x   #balance = 8
        t2:x = balance - n   #0
        t2:balance = x    #0

    2.实际情况下:
        t1:x1 = balance + n  #x1 = 5

        t2:x2 = balance + n   #x2 = 8
        t2: balance = x2    #balance = 8

        t1:balance = x1   #balance = 5
        t1:x1 = balance - n  #x1 = 0
        t1:balance = x1   #balance = 0

        t2:x2 = balance - n #x2 = -8
        t2:balance = x2   #balance = -8

    总结:修改balance需要多条语句,执行多条语句的时候,线程随时可能会被中断,从而导致多个线程将一个变量修改乱了

解决:上锁【给多线程中的临界资源上一把锁,当一个线程访问的时候,其他的线程则必须在锁外面进行等待,直到锁被释放之后,
其他线程才有再次争抢时间片的机会】
"""

线程锁

from  threading import  Thread,Lock

balance = 0

#创建一个锁对象
lock = Lock()

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子线程的任务
def run(n):
    for _ in range(1000000):
        #获取锁
        lock.acquire()

        try:
            # 临界资源
            change(n)
        finally:
            #释放锁
            lock.release()


if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理论上,balance最终的结果为0
    print(balance)
from  threading import  Thread,Lock

balance = 0

#创建一个锁对象
lock = Lock()

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子线程的任务
def run(n):
    for _ in range(1000000):
        #同样表示获取锁,但是,不用手动释放,当临界资源的代码执行完毕之后,锁会被自动释放
        with lock:
            # 临界资源
            change(n)

if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理论上,balance最终的结果为0
    print(balance)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值