python_thread

多任务编程:可以有效的利用计算机资源,同时执行多个任务
进程:进程就是程序在计算机中一次执行的过程
进程 和 程序的区别:
    1、程序是一个静态文件的描述,不占计算机的系统资源
    2、进程是一个动态的过程,占有cpu、内存等资源,有一定的生命周期
注意:同一个程序的不同执行过程即为不同的进程
问题1、什么决定了进程的创建
    答:用户通过应用层程序进行进程的创建申请-->调用操作系统接口进行进程创建
        -->告知系统内核创建新的进程提供给应用层使用
问题2、进程如何占有CPU
    1、同一个内核同一时刻只能运行一个进程
    2、多个进程对内核资源进行抢占,由操作系统内核进行分配
    3、哪个进程占有计算机内核,我们称为该进程占有CPU的时间片
问题3、进程在运行过程中的形态和附带内容
    1、PCB(进程控制块):在linux和unix操作系统中,进程创建后会在内存中开辟一块空间存放进程的相关信息,这个空间称之PCB
    2、PID:在操作系统中进程的唯一标识,是一个大于0的正整数,由系统自动分配
       ps -aux 查看进程信息
    3、虚拟内存:每个进程占有4G内存地址空间,这里的内存指的是虚拟内存
    4、进程状态
        1、三态:
            1、就绪态:进程具备运行条件,等待系统分派处理器以便运行
            2、运行态:进程占有CPU处于运行状态
            3、等待态:又称为阻塞态或睡眠态,指进程不具备运行条件,正在等待某些条件的达成

        2、五态:在三态的基础上添加两个状态新建态、终止态
            1、新建态:创建一个进程的过程,直接表现为执行某个程序或在程序中创建新的进程
            2、终止态:进程执行结束,完成回收的过程

        D:等待态(不可中断,不会被外界条件所影响)
        S:等待态(可中断,会被外界条件所影响)
        T:等待态(暂停,无条件的等待)
        R:运行态
        Z:僵尸态
            上面只能选一个,后面的是配合上面使用的
        +:前台进程
        N:低优先级进程
        <:高优先级进程
        l:有进程连接
        s:会话组
进程的优先级:
    1、优先级往往决定了一个进程的执行权限和占有系统资源的优先程度
    2、linux系统中优先级范围 -20~19 -20优先级最高
    3、用户创建的进程默认优先级为0
    4、top:动态查看系统进程运行情况,(NI字段值就是优先级)
       <  > 可以用来翻页查询
    5、nice:以指定的优先级运行某个进程
        示例: nice -9 ./while.py  #以9的优先级运行程序
             sudo nice -9 ./while.py #以-9的优先级运行程序
    6、renice n PID:修改一个正在运行的进程的优先级
        示例:renice 8 12836 :将12836号的进程优先级修改为8

    7、PR:也表示进程的优先级,系统为了控制用户可以修改的优先级范围;PR的范围在:-99~39 与NI的关系:PR = NI + 20

作业控制jobs:

  1、jobs:查看后台进程

  2、fg:让后台进行在前台运行

  3、bg:让暂停的进行在后台运行

  4、ctrl + z:让前台的进程在后台暂停

虚拟文件系统:

  CPU:/proc/cpuinfo

  内存:/proc/meminfo

  内核: /proc/cmdline

父子进程:在系统中除了初始化进程之外每个进程都是由父进程创建的,每个进程有一个唯一的父进程,可能有多个子进程
        pstree:查看进程树
总结:
    1、什么是进程:程序在计算机中一次执行的过程
    2、进程和程序的区别:进程是动态的占有cup内存资源,程序是静态文件,不占有从cpu内存资源
    3、进程的集中状态及相互间的转换
    4、什么是PCB PID CPU时间片

需求:两件不相关的事情希望同时来做

方案1:创建两个进程,分别承担不同的事情,各自执行
分析:
    1、两个程序比较麻烦
    2、无法确定两个程序应该在什么时间开始运行
方案2:写一个程序,在程序中指定位置用接口来创建新的进程

    实现方法:
        1、os.fork()函数实现
fork()
    1、功能:创建一个新的进程
    2、参数:无
    3、返回值:
        1、<0表示进程创建失败
        2、==0在子进程中fork的返回值为0
        3、>0在父进程中fork的返回值大于0
fork 是os模块只能在linux和Unix系统下使用

#os 模块提供大量和系统相关的功能函数接口
#os 模块的使用是与系统相关的,在不同的系统中可能有不同的功能
import os

print('before craete process')
a = 10
pid = os.fork()
if pid < 0:
    print("Create process failed")
elif pid == 0:
    print('This is the new process')
    print(a)#a=10:父进程中fork之前的内容,子进程同样也会复制,但是父子进程空间内容的修改不会相互影响
    print(os.getpid())
    a = 1000
else:
    print('This is the parent process')
    print(a)#a=10:但是父子进程空间内容的修改不会相互影响
    print(pid)#pid的值等于子进程的PID
print("The process end")
View Code

测试1:父进程中fork之前的内容,子进程同样也会复制,但是父子进程空间内容的修改不会相互影响
测试2:父子进程在执行上互不影响,理论上不一定谁先执行
测试3:子进程虽然复制父进程的空间,但是也有自己独特的特性,比如:自己的PID,进程控制块,进程栈等,父进程中的fork的返回      值即为创建子进程的PID号
进程相关的函数:
    1、os.getpid():       获取当前进程的PID号
    2、os.getppid():       获取当前进程父进程的PID号
    3、os._exit(status)  
        1、功能:结束一个进程
        2、参数:一个数字(必须是整数)表示进程的退出状态,通常0表示正常退出,其它数字表示非正常退出
    4、sys.exit([status])  
        1、功能:结束一个进程,如果处理了抛出异常,则不结束进程
        2、参数:一个数字表示进程的退出状态,通常0表示正常退出,其它数字表示非正常退出,
               还可以是一个字符串,如果是字符串则退出进程会打印这个字符串

import os
import sys

# os._exit(0)
try:
    #如果处理了抛出异常,则不结束进程
    sys.exit(0)#参数可有可无,如果是字符串会打印这个字符串(小数视为字符串),
except SystemExit as e:
    print(e)    #次数的参数就是exit传进去的参数

print('process over')
View Code

僵尸进程:
    1、定义:子进程先于父进程退出,父进程没有对子进程的退出做相应的处理,此时子进程就会变成僵尸进程
    2、影响:进程退出后,仍有部分信息残留在内存空间,大量的僵尸进程会影响系统运行,    所以应该尽量避免僵尸进程的产生

孤儿进程:
    1、定义:父进程先于子进程退出,此时子进程就会变为孤儿进程
    2、影响:当一个进程变为孤儿进程,系统会自动的使用一个进程成为孤儿进程的父进程。当孤儿进程退出时,该系统进程会自动
            回收孤儿,使他不会成为僵尸,所以孤儿进程对系统资源没有什么影响
处理僵尸进程的方法:
    1、让父进程先退出(不好控制)
    2、父进程处理子进程的退出
        1、os.wait():
            1、功能:等待子进程退出进行处理
            2、参数:无
            3、返回值:返回一个包含两个元素的元组,第一个是退出的子进程的PID号,第二个是子进程的退出状态
            4、wait是一个阻塞函数 即 进程处于等待态,等待某种条件的达成才会继续运行

import os
import sys
from time import *

pid = os.fork()
if pid < 0:
    print('create process failed')
elif pid == 0:
    print('Child process...')
    print('Child_PID:',os.getpid())
    sleep(2)
    sys.exit("ok11")#子进程退出
else:
    #wait阻塞等待子进程的退出
    p,status = os.wait()#p等于子进程的PID,status等于exit参数为正值时的值乘以256
    print(p,status)#如果退出状态是字符串时,status的值为256
    print(os.WEXITSTATUS(status))#等于子进程退出的状态
    print('Parent process..')
View Code

        2、os.waitpid(pid,option)
            1、功能:同wait 处理子进程退出使其不会变成僵尸
            2、参数:pid=-1时 表示等待任意子进程退出
                    pid>0时 表示等待指定进程号的子进程退出
                    option=0时 表示阻塞等待
                    option = os.WNOHANG时 表示 非阻塞等待

import os
import sys
from time import *

pid = os.fork()
if pid < 0:
    print('create process failed')
elif pid == 0:
    print('Child process...')
    print('Child_PID:',os.getpid())
    sleep(2)
    sys.exit(1)#子进程退出
else:
    #waitpid非阻塞等待子进程的退出:如果执行到这里刚好这时子进程退出则处理,否则不处理
    p,status = os.waitpid(-1,os.WNOHANG)#p等于子进程的PID,status等于exit参数为正值时的值乘以256
    print(p,status)
    print(os.WEXITSTATUS(status))#等于子进程退出的状态
    print('Parent process..')
View Code

  3、创建二级子进程

#创建二级子进程解决僵尸进程
import os

pid = os.fork()
if pid < 0:
    print('create process failed')
elif pid == 0:
    p = os.fork()#创建二级子进程
    if p < 0:
        print('process failed')
    elif p == 0:
        print('做二级子进程任务')
    else:
        os._exit(0)#一级子进程退出,使二级子进程变成孤儿
else:
    os.wait()#等待一级子进程退出
    print('做父进程该做的')
View Code

  4、在父进程中使用信号处理的方法忽略子进程发来的信号
    当子进程结束时会发给一个SIGCHLD信号给父进程,所以在父进程中添加signal(SIGCHLD,SIG_IGN)

import os
import time
import signal

pid = os.fork()
if pid < 0:
    print('create process failed')
elif pid == 0:
    print('子进程的PID:',os.getpid())

else:
    print('父进程的PID:',os.getpid())
    #子进程结束时会发送SIGCHLD信号给父进程,此时父进程忽略(就当没有什么都没有发生),此时子进程就不会变为僵尸进程
    signal.signal(signal.SIGCHLD,signal.SIG_IGN)
    time.sleep(30)
    print("父进程结束")
View Code

总结:
    1、函数的使用 fork()/getpid()/getppid()/os._exit/sys.exit/wait/waitpid
    2、理解什么是僵尸进程和孤儿进程即两者产生的过程
    3、知道僵尸进程的危害和两种处理方法
    4、理解进程的创建流程

 

更方便高效的进程创建方法
multiprocessing模块(标准库模块)
创建进程的步骤:
    1、将要完成的事件封装成一个个函数
    2、使用multiprocessing提供的接口函数创建进程
    3、使新的进程和指定的函数相关联去完成函数中的工作
    4、对进程进行回收处理
注意:
    1、函数当赋给Process的target变量后函数内容就是对应子进程的进程内容了,此时函数才有特殊性
    2、多个子进程和父进程之间的执行互相不影响

import multiprocessing as mp 
import os
import time
#创建三个函数,即为将要我完成的事件
def th1():
    print(os.getppid(),'---',os.getpid())
    print('吃早饭')
    time.sleep(1)
    print('出午饭')
    time.sleep(2)
    print('吃晚饭')
    time.sleep(3)

def th2():
    print(os.getppid(),'---',os.getpid())
    print('睡午觉')
    time.sleep(1)
    print('睡觉')
    time.sleep(3)

def th3():
    print(os.getppid(),'---',os.getpid())
    print('打豆豆')
    time.sleep(2)
    print('打豆豆')
    time.sleep(2)
#创建3个子进程,生成子进程对象p1/p2/p3
#使用multiprocessing提供的接口函数创建进程,使新的进程和指定的函数相关联去完成函数中的工作
p1 = mp.Process(target = th1)
p2 = mp.Process(target = th2)
p3 = mp.Process(target = th3)
#启动进程让其执行对应的函数事件,该函数事件即为这个进程的内容
p1.start()
p2.start()
p3.start()
print('ParentProcess',os.getpid())
#阻塞等待子进程的退出,然后回收子进程,对进程进行回收处理
p1.join()
p2.join()
p3.join()
print("**********************")
View Code

创建子进程:Process()类
    1、参数:

    target         指定要绑定的函数
            name         给创建的进程起一个名字
            args           需要一个元组,给target指定的函数传参,按位置传参
            kwargs       需要一个字典,给target指定的函数按键值传参
    2、进程对象的属性和方法:
        1、'进程名称',p.name
        2、'进程PID',p.pid
        3、'进程状态',p.is_alive()

from multiprocessing import Process
import time
a = 1
def worker(sec,msg):
    #当worker作为子进程运行时,对全局变量a的修改只会影响在子进程中a的值,对父进程没有影响
    global a
    a = 1000
    for i in range(3):
        time.sleep(sec)
        print('the worker msg:',msg)
    print(a)
#args元组中的2将会传给sec,kwargs字典的值传给msg
p = Process(name = 'worker',target=worker,args=(2,),kwargs={'msg':'You are a big man'})
p.start()
#进程名称
print('进程名称',p.name)#worker
print('进程PID',p.pid)#3429
print('进程状态',p.is_alive())#True
p.join()
print("parent's a :",a)#parent's a : 1
View Code

        4、daemon 默认值为False,表示主进程运行结束后,不会影响子进程的运行,直到子进程运行完,进程才会结束,如果设    置为True,则主进程运行完毕,所有的子进程也不再运行。哪个子进程设置为True则在主进程结束后该子进程也停止执行,没有设置的子进程照常执行,在这一点上与线程中的daemon是有所不同的
            该属性的设置必须要在start()前

    该属性如果设置为True,则不再使用join
            该属性的设置并不是将进程设置为linux、Unix中的守护进程
            守护进程:生命周期长,与前端控制台无关,后台运行一般用作系统进程或者自动化运行进程
启动子进程:start()
    1、start()时才真正的创建子进程,而不是Process时创建的
回收子进程:join([timeout])
    1、timeout:设置最长阻塞时间,如果超时这个时间还没有子进程退出则不再继续等待
  内核会帮助应用层记录子进程的退出情况,当使用join函数时内核会及时返回进程状态给应用层进行处理

import multiprocessing as mp
from time import sleep

def fun():
    print('Child process is start')
    sleep(3)
    print('Child proess is end')

p1 = mp.Process(target = fun)
p2 = mp.Process(target = fun)

p1.daemon = True#必须在start之前设置,哪个子进程设置为True则在主进程结束后该子进程也停止执行,
p1.start()#没有设置的子进程照常执行
p2.start()

sleep(1)
print('******main process over*****')
View Code

多进程编程
    1、优点
        1、可以并行的执行多个任务,提高运行效率,空间独立,数据安全
        2、创建方便
    2、缺点:
        1、进程的创建和销毁过程需要消耗较多的计算机资源
   2、在需要频繁的创建和删除较多进程的情况下,资源消耗过多,不适宜使用多进程完成任务

进程池技术

 产生原因:

  如果有大量任务需要对进程完成,且可能需要频繁的创建和删除进程,给计算机带来大量的资源消耗。

 原理:

  在进程池内运行一定数量的进程,通过这些进程完成进程池队列中的事件,直到事件执行完毕,减少进程不断创建和删除过程

 实施操作方法
      1、创建进程池,在池内放入适量的进程
      2、将事件加入进程池的等待队列
      3、使用进程池内的进程不断的执行等待事件
      4、所有事件处理结束后关闭回收进程池

Pool(processes):
    1、功能:创建进程池
    2、参数:processes:进程池中进程的数量,默认为根据运行环的核数确定,并不是进程越多越高效
    apply_async()
        1、功能:以异步的方式将要执行的事件放入进程池,非阻塞
        2、参数:
            1、func: 要执行的函数
            2、args: 给函数按位置传参
            3、kwds:给函数按照键值传参
        3、返回值:返回事件执行后的返回值对象,可以通过调用get()函数获取事件函数return的内容
    apply()
        1、功能:按照顺序添加要执行的事件,执行完一个事件在执行下一个事件,失去了进程池的意思,一般不用,阻塞

  ·2、参数:
            1、func: 要执行的函数
            2、args: 给函数按位置传参
            3、kwds:给函数按照键值传参

  ·3、没有返回值
    close()
        1、功能:关闭进程池,使其不能再加入新的事件,但是之前的添加的要执行完
        2、参数:无
    join()
        1、功能:等待所有子进程结束并处理
        2、参数:无

import multiprocessing as mp 
from time import sleep
import os

def worker(msg):
    sleep(2)
    print(msg)
    return 'worker return %s'%msg

#创建进程池对象,并放入4个的进程,processes是固定的
pool = mp.Pool(processes = 4)

results = []
for i in range(10):
    msg = 'hello%d'%i
    #向进程池加入要执行的事件,不需要start就直接开始执行
    r = pool.apply_async(worker,(msg,))
    results.append(r)

for res in results:
    print(res.get())#get方法得到进程时间的返回值,即work函数的返回值
#关闭进程池的事件加入通道,即不能在向进程池中添加事件
pool.close()
#阻塞等待进程池处理事件结束后回收进程池
pool.join()
进程池

    map()
        1、功能:类似于内建函数map,将第二个参数的迭代对象中的数据逐个传入第一个函数作为参数。只不过兼顾了apply_async功能,将函数放入进程池

   2、返回值:事件函数的返回值列表

from multiprocessing import Pool
import time

def fun(fn):
    time.sleep(1)
    return fn*fn
test = [1,2,3,4,5]
print('顺序执行:')
s = time.time()
for i in test:
    fun(i)
e = time.time()
print('执行时间:',e-s)#5.004135608673096

pool = Pool(processes = 4)
r = pool.map(fun,test)#test参数需要是一个可迭代对象
pool.close()
pool.join()
print('执行事件',time.time()-e)#2.0332601070404053
进程池中map的用法

创建自己的进程类
    1、继承Process类以获取原有的属性
    2、实现自己需要的功能部分
    3、使用自己的类创建进程即可

from multiprocessing import Process
import time

class ClockProcess(Process):
    def __init__(self,value):
        super().__init__()#等价于Process.__init__(self),执行父类中的__init__方法
        self.value = value

    def run(self):
        '''在Process类中实现的,现在重写该方法'''
        n  = 5
        while n >0:
            print('The time is {}'.format(time.ctime()))
            time.sleep(self.value)
            n -= 1
#使用自己的类创建两个进程
p1 = ClockProcess(2)
p2 = ClockProcess(2)
#start后会自动执行run()函数
p1.start()
p2.start()
p1.join()
p2.join()
类进程

进程间的通信:不同的进程间进行数据的传输
    实现方法1:文件进行进程间的通信(和磁盘交互慢,数据不安全)
 可行的实现方法:
       管道   消息队列    共享内存   信号   套接字 等

   1、管道:
        在内存中开辟一个管道空间,对多个进程可见,在通信形式上形成一种约束
        linux系统下的文件类型:b(块文件)c(设备文件)d(目录)-(普通文件)l(链接)s(套接字)p(管道)
        实现方法:multiprocessing---->Pipe函数
        Pipe(duplex)
            1、功能:创建一个管道
            2、参数:duplex默认为True 表示管道为全双工(双向管道),如果为False则表示管道为半双工(单向管道)
            3、返回值:返回两个管道流对象,分别表示管道的两端。如果参数为True(默认)两个对象均可发送接收,如果为False则第一个对象只能接收,第二个对象只能            发送。
            4、特别注意:
                1、向管道发送数据使用send()函数,从管道接收数据使用recv()函数
                2、recv()函数为阻塞函数,当管道中数据为空的时候会阻塞
                3、一个recv()只能接收一次send()发送的内容
                4、send()可以发送字符串、数字、列表等多种类型的数据

from multiprocessing import Pipe,Process
import time
import os

#创建管道对象
child_conn,parent_conn = Pipe()
#创建子进程函数
def fun(name):
    time.sleep(1)
    #发送一个字符串到管道
    print(os.getppid(),'----',os.getpid())
    child_conn.send('hello'+str(name))
#创建5个子进程,并放入一个列表中    
jobs = []
for i in range(5):
    p = Process(target = fun,args=(i,))
    jobs.append(p)
    p.start()
#冲管道中读取数据,一次只能读一条数据
for i in range(5):
    data = parent_conn.recv()
    print(data)
# time.sleep(3)
# data = parent_conn.recv()#只能取到一条数据
# print(data)
#等待子进程结束
for job in jobs:
    job.join()
管道

  2、消息队列:

    1、在内存中开辟一个队列模型,用来存放消息,任何拥有队列对象的进程都可以进行消息的存放和取出
       2、实现方法:multiprocessing--->Queue函数
       Queue(maxsize=0)
            1、功能:创建一个消息队列对象
            2、参数:maxsize 默认为0,表示消息队列可以存放的消息由系统自动分配的空间而定,如果是大于0的正整数,表示队列中最多存放多少条消息
            3、返回值:消息队列对象
       q.put():
            1、向消息队列存放一条消息,当消息队列满的时候,会阻塞;存放的消息类型可以是数字、列表,字符串等
       q.full():
            1、判断队列是否满了,如果满了返回True,否则返回False
       q.qsize():
            1、返回当前队列中消息数量
       q.get():
            1、获取消息,每次获取一条,当消息队列为空时,则阻塞
       q.empty():
            1、消息队列为空时返回True,否则返回False
       put 和 get中block参数和timeout参数:
            1、block 默认为True 表示两个函数都是阻塞函数,如果设置为Fasle则表示不阻塞
            2、timeout 当 block设置为True的时候表示超时等待时间

from multiprocessing import Queue

#创建消息队列对象
q = Queue(3)
#存放消息
i = 0
while True:
    #判断队列是否满了
     if q.full():
         print('queue is full')
         break
     q.put('hello'+str(i))
     i += 1
print("当前队列中的消息数:",q.qsize())

for i in range(q.qsize()):
    print('消息是:',q.get())
print('是否为空:',q.empty())
try:
    q.get(True,4)#当队列中没有数据时get函数会阻塞4秒,之后会抛出异常
except Exception as e:
    print(e)
print("proces is over ")
消息队列
from multiprocessing import Process,Queue
import time

q = Queue()

def fun(name):
    time.sleep(1)
    q.put('hello'+str(name))
    
jobs = []
for i in range(10):
    p = Process(target=fun,args=(i,))
    jobs.append(p)
    p.start()
for job in jobs:
    job.join()

while not q.empty():
    print(q.get())
消息队列

 3、共享内存:
        在内存开辟一段内存空间存储数据,每次存储的内容会覆盖上次的内容。由于没有对内存进行格式化(对数据进行整理排放)的修饰所以存取速度块的效率高
        实现方法1:multiprocessing--->Value
        实现方法2:multiprocessing--->Array
        1、obj = Value(ctype,obj):
            1、功能:开辟新共享内存
            2、参数:

         1、ctype:要转变的c语言的类型
                     2、obj: 要写入共享内存的初始值
            3、obj.value: 属性获取共享内存中的值

from multiprocessing import Value,Process
import time
import random

def deposite(money):
    '''存钱函数,将作为一个进程的事件'''
    for i in range(100):
        time.sleep(0.03)
        money.value += random.randint(1,200)

def withdraw(money):
    '''取钱函数,将作为一个进程的事件'''
    for i in range(100):
        time.sleep(0.02)
        money.value -=random.randint(1,150)
#开辟一个共享内存
money = Value('i',2000)#将python数据类型转换为C语言的数据类型
d = Process(target=deposite,args=(money,))
w = Process(target=withdraw,args=(money,))
d.start()
w.start()
d.join()
w.join()
print(money.value)#取出共享内存中的数据,这个数据是最后一次放进去的数据
共享内存value

        2、 obj = Array(ctype,obj)
            1、功能:开辟一个共享内存空间
            2、参数:

        1、ctype 要转变的c语言的数据类型
                     2、obj:要放入共享内存中的数据,是一个列表,要求列表中的数据为相同类型的数据。

            如果obj为一个正整数,则表示在共享内存中开辟一个多大的空间,空间可以存放的数据类型由ctype确定
        3、返回值:返回一个可迭代对象可通过for循环取值,可以进行修改值

from multiprocessing import Array,Process
import time
import ctypes

def fun(shm):
    for i in shm:
        print(i)
    shm[0]=1000
#shm = Array('i',[1,2,3,4,5])#第二个参数列表中的数据必须要一致
shm = Array(ctypes.c_char,5)#如果存入的是正整数,则表示在共享内存中开辟一个多大列表(默认列表中的值全为0)
p = Process(target = fun,args=(shm,))

p.start()
p.join()

for i in shm:
    print(i)
共享空间array
  管道 消息队列  共享空间
开辟空间 内存中 内存中 内存中
读写方式 可双向/单向 先进先出,按照个数储存 直接操作内存
效率 一般 一般 较快
是否需要同步互斥 不需要 不需要 需要


  4、信号:信号是一种异步的进程间的通信方式
        kill -l :查看系统信号
        kill -signame PID:  给进程号PID的进程发送signame信号

     信号的三要素:

      1、信号名称:系统定义

      2、含义 :系统定义

      3、 默认处理方法:

        1、采用默认方式处理(系统定义:终止、暂停、忽略)

        2、忽略信号(当信号没有发生过)

        3、采用自定义的方式处理
    如何发送信号:
        1、os.kill(pid,sig)
            1、功能:向一个进程发送一个信号
            2、参数:pid:要发送进程的PID号;sig:要发送的信号

import os
import signal

os.kill(19513,signal.SIGKILL)
View Code

        2、signal.alarm(sec)
            1、功能:给自己发送一个时钟信号(SIGALRM)
            2、参数 sec:秒数表示在相应的秒数后发送时钟信号
         3、alarm函数在一个进程中如果使用多次,则后面的时钟时间会覆盖前面的时间

import signal
import time

#3秒后给自己发送一个SIGALRM信号
signal.alarm(3)
print('ok')
signal.alarm(8)#此处的时间会覆盖掉前面的时间
while True:
    time.sleep(1)
    print('等待时钟信号')
View Code

    信号的处理:
        1、signal.pause()
            1、功能:阻塞等待一个信号的发生
        2、signal.signal(signum,handler)
            1、功能:处理信号
            2、参数:

        1、signum:表示可以处理的信号;

        2、handler:信号的处理方法:
                  1、默认处理方式:SIG_DFL
                  2、忽略信号:SIG_IGN
                  3、自定义方式:function
           3、signal函数也是一个异步处理信号的函数
           4、SIGSTOP 和 SIGLILL不能被signal函数处理

'''
1、创建父子进程,分别表示司机和售货员
2、当售票员捕捉到SIGINT(ctrl+c)信号时给司机发送SIGUER1信号,司机打印‘老司机开车了’
3、等售票员捕捉到SIGQUIT(ctrl+\)信号时给司机发送SIGUSR2信号,司机打印‘系好安全带,小心甩出去’
4、当司机捕捉到SIGTSTP(ctrl+z)信号时给售票员发送SIGUSR1信号,售票员打印‘到站了,下车吧’
5、到站后售票员先下车(子进程先退出),然后司机下车
'''
from multiprocessing import Process,Pipe
import signal
import os 
import time

def conductor():
    '''子进程函数即子进程事件'''
    signal.signal(signal.SIGINT,handler)
    signal.signal(signal.SIGQUIT,handler)
    signal.signal(signal.SIGUSR1,handler)
    #子进程忽视SIGTSTP信号
    signal.signal(signal.SIGTSTP,signal.SIG_IGN)
    while True:
        time.sleep(1)
        print('running.....')

def handler(sig,frame):
    '''子进程收到信号做相应的处理'''
    father_PID = os.getppid()
    if sig == signal.SIGINT:
        os.kill(father_PID,signal.SIGUSR1)
    elif sig == signal.SIGQUIT:
        os.kill(father_PID,signal.SIGUSR2)
        
    elif sig == signal.SIGUSR1:
        print('到站了下车吧')
        os._exit(0)

def father_handler(sig,frame):
    '''父进程得到信号做出相应的处理'''
    if sig == signal.SIGUSR1:
        print('老司机开车了')
    elif sig == signal.SIGUSR2:
        print('系好安全带,小心甩出去')
    elif sig == signal.SIGTSTP:
        os.kill(p.pid,signal.SIGUSR1)


p = Process(target=conductor)
p.start()
#接收子进程传递过来的信号
signal.signal(signal.SIGUSR1,father_handler)
signal.signal(signal.SIGUSR2,father_handler)
signal.signal(signal.SIGTSTP,father_handler)
#主进程收到一下信号,均忽视掉
signal.signal(signal.SIGINT,signal.SIG_IGN)
signal.signal(signal.SIGQUIT,signal.SIG_IGN)
p.join()
View Code

同步和互斥:
    1、临界资源:对多个进程或者线程都可见的资源,容易产生争夺,我们将这类资源称为临界资源
    2、临界区:对临界资源进行操作的代码区域称之为临界区
    3、同步 或者 互斥来解决资源争夺
    4、同步:同步是一种合作关系,为完成某种任务而建立的多个进程或者线程之间的协调,次序等待,传递消息告知资源占用情况
    5、互斥:互斥是一种制约关系,当一个进程或者线程进入到临界区会进行枷锁操作,此时其他进程或线程无法进入临界区,只有    
           当该进程或线程使用后解锁,其他资源才可以使用,这种技术往往是通过阻塞完成   
Event事件:
    Event函数
        1、e.wait():产生一种阻塞,直到e被set之后才能结束阻塞
        2、e.set():将e set操作,wait不再阻塞
        3、e.is_set():判断e是否是被设置的状态,被设置返回True,wait不再阻塞,没被设置返回空
        4、e.clear():将e变成没有被设置的状态

from multiprocessing import Process,Event
import time

def wait_event():
    print('proces1 is running')
    e.wait()
    print('proces1 is running... ')

def wait_event_timeout():
    print('proces2 is running')
    e.wait(2)
    print('timeout,proces2 is running...',e.is_set())

e = Event()
p1 = Process(name='block',target=wait_event)
p2 = Process(name='non_block',target=wait_event_timeout)
p1.start()
p2.start()
print('parent is running')
time.sleep(3)
e.set()#将e set操作,wait不再阻塞
print('parent is over')
Event
from multiprocessing import Event

#生成时间对象
e = Event()
#检测时间对象,如果被设置返回True,否则返回False
print(e.is_set())#False
#设置事件对象
e.set()
print(e.is_set())#True
#提供事件的阻塞
e.wait(3)
print('wait....')
#清除事件的阻塞
e.clear()
print(e.is_set())#False

e.wait(3)
print('&&&&')
Event

进程间同步互斥方法:
    multiprocessing--->Lock
    lock=Lock()
        1、功能:创建进程锁对象
    lock.acquire()
        1、功能:给临界区上锁
        2、具体实现上有一个条件阻塞函数,当有一个进程先进行了acquire操作后,其他进程再企图进行acquire操作时就会阻塞,直到lock对象被release后,其他进程才可以进行下次acquire操作
    lock.release()
        1、功能:给临界区解锁    

 说明:

  1、可以将lock对象放入到with语句中,当语句开始执行时就会加锁,语句执行结束后自动解锁

  2、如果一个进程没有acquire和release(不加锁解锁),则这个进程可以操作这个临界区

from multiprocessing import Process,Lock
import time
import sys

def worker1(stream):
    lock.acquire()#加锁
    for i in range(5):
        time.sleep(1)
        stream.write("Lock acquire via\n")
    lock.release()#解锁
def worker2(stream):
    #lock.acquire()
    with lock:#放在with语句块中,语句块开始即加锁,结束即解锁
        for i in range(5):
            time.sleep(1)
            stream.write('Lock acquire  directly\n')
    #lock.release()
lock = Lock()
w1 = Process(target = worker1,args=(sys.stdout,))
w2 = Process(target = worker2,args=(sys.stdout,))

w1.start()
w2.start()

w1.join()
w2.join()
Lock

线程
    1、线程也可以使用计算机的多核资源,也是多任务编程方式之一
    2、线程又称轻量级的进程,在并发上和进程相同但是在创建时消耗资源少
    3、一个进程可以包含多个线程,这多个线程共享进程的资源
    4、多个线程因为共享进程资源所以在通信上往往采用全局变量的方法
    5、线程也拥有自己特有的资源,比如TID、指令集等
多进程和多线程的区别和联系:(非常重要需要熟记)
    1、都是多任务编程,都可以使用计算机多核
    2、进程的创建要比线程消耗更多的资源
    3、进程空间独立数据更安全,有专门的进程间通信方式进行交互
    4、一个进程包含多个线程,所有线程共享进程资源,没有专门的通信方法,依赖全局变量进行通信。往往需要使用同步互斥机制,逻辑需要考虑更多
    5、进程和线程都有自己特有的资源。多个关联任务的时候使用多线程资源消耗更少,如果是多个无关任务也不适于全都使用线程

 6、线程是CPU调度和分派的基本单位,进程是操作系统的基本单位
创建线程:
    1、threading
    2、threading.Tread()
        1、功能:创建线程
        2、参数:
                1、target 线程函数
                2、args:以元组方式给线程函数传参
                3、kwargs:以字典方式给线程函数传参
                4、name:线程名称(默认Thread—1)

import threading
from time import ctime,sleep

a = 10
def music(sec):
    print('Listening music')
    sleep(sec)
    global a
    print(a)
    a = 10000
t = threading.Thread(name='my_thread',target=music,args=(2,))
t.start()
print('创建线程')
sleep(3)
print(a)#1000
Thread

        3、返回值:返回线程对象
        4、t.start(): 启动一个线程
        5、t.is_alive():查看一个线程的状态
        5、t.name:        查看线程的名字
        6、t.join([sec]):阻塞等待回收线程
        7、daemon属性:

      1、该默认为False主线程执行完毕不会影响其他线程的执行,如果设置为True则主线程执行完毕其他线程也终止执行,

      2、如果要是有多个子线程,则每个子线程都要设置为True才会生效,只要有一个没有设置则全都无效
            1、t.setDaemon(True)
            2、t.daemon=True
            3、t.isDaemon():获取属性值

import threading
from time import sleep, ctime
def fun():
    print('This is s thread1 test')
    sleep(5)
    print('thread1 over')
def fun2():
    print('This is s thread2 test')
    sleep(5)
    print('thread2 over')

t1 = threading.Thread(target=fun,name='my_thread1')
t2 = threading.Thread(target=fun2,name='my_thread2')

#t1.setDaemon(True)
t1.daemon = True
t2.daemon = True
print('t1_daemon:',t1.isDaemon())
print('t2_daemon:',t2.isDaemon())
t1.start()
t2.start()

print('t1_name: ',t1.name)
print('t1_isalive: ',t1.is_alive())#线程的状态
print('t2_name: ',t2.name)
print('t2_isalive:',t2.is_alive())#线程的状态

t1.join(2)
print('all over ',ctime())
View Code

线程间的通信:
    1、全局变量进行通信

'''线程间是通过全局变量进行通信,如果不实现同步互斥方法,会造成数据的混乱'''
import threading
from time import sleep

s = None
def bar():
    print("呼叫foo")
    global s
    s = '天王盖地虎'
def foo():
    print('foo等口令')
    sleep(2)
    print("收到口令",s)

def fun():
    sleep(1)
    print('内奸出现')
    global s
    s = '小鸡炖蘑菇'

t1 = threading.Thread(target= bar,name='bar')
t2 = threading.Thread(target=foo,name='foo')
t3 = threading.Thread(target=fun,name='fun')

t1.start()
t2.start()
t3.start()

t1.join()
t2.join()
t3.join()
View Code

    2、线程间的同步互斥方法
        1、线程 Event
            1、e = threading.Event()
            2、e.wait([timeout]):如果e被设置则不会阻塞,未被设置则阻塞,timeout为阻塞的超时时间
            3、e.set() :将e变为设置的状态
            4、e.clear():将e变为未设置的状态

from threading import *
import random
from time import sleep

a = 500
#创建事件对象
e = Event()
def fun():
    '''子线程不断减少a,但是不希望a的值小于0'''
    while True:
        global a
        sleep(2)
        print('a=',a)
        e.wait()
        a-=random.randint(0,100)

t = Thread(target=fun)
t.start()
#住线程不断增加a,以确保a的值不会小于0
while True:
    sleep(1)
    a+=random.randint(0,10)
    if a >100:
        e.set()#让wait不阻塞
    else:
        e.clear()#让wait阻塞

t.join()
View Code

        2、线程锁
            1、lock=threading.Lock()
            2、lock.acquire(): 上锁
            3、lock.release():解锁

import threading
import time

a = b =0

lock = threading.Lock()

def value():
    while True:
        time.sleep(1)
        lock.acquire()
        if a != b:
            print('a=%d,b=%d'%(a,b))
        lock.release()
t = threading.Thread(target=value)
t.start()
lock.acquire()
while True:
    a +=1
    b +=1
lock.release()
t.join()
View Code

创建自己的线程类:
    1、自定义类继承原有线程类Thread
    2、重写原有的run方法
    3、创建线程对象调用start的时候会自动执行run放方法
 4、threadpool 线程池第三方模块:
      安装方法: sudo pip3 install threadpool

import threading 
from time import ctime,sleep

class MyThread(threading.Thread):
    def __init__(self,func,args,name='levi'):
        super().__init__()
        self.func = func
        self.args = args
        self.name = name

    #自定义线程启动函数
    def run(self):
        self.func(*self.args)

def player(file,time):
    for i in range(2):
        print('start playing%s:%s'%(file,ctime()))
        sleep(time)

t = MyThread(player,('baby.mp3',3))
t.start()
t.join()
View Code

GIL(全局解释器锁)
    1、python-->支持多线程-->同步和互斥-->加锁-->超级锁-->解释器在同一时刻只能解释一个线程-->大量python库为了省事依赖于这种机制-->python多线程效率低
    2、GIL即为python解释器由于上锁带来的同一时刻只能解释一个线程的问题
    3、解决方案:
        1、不使用线程,转而使用进程
        2、不使用c作为解释器,使用Java c#做为python解释器
IO密集型:程序中进行了大量IO操作,只有少量的CPU操作
    1、在内存中进行了数据交换的操作都可以认为是IO操作
    2、特点:速度较慢,使用CPU不高

cpu密集型(计算密集型):大量的程序都在进行运算操作
    1、特点:CPU占有率高
效率测试:
    多线程的工作效率和单线程几乎相近,而多进程要比前两者有明显的效率提升

设计模式:
    1、设计模式代表了一种最佳实践,是被开发者长期总结,用来解决某一类问题的思路方法。这些方法保证了代码的效率也易于理解,比如:单例模式、工厂模式、生产者模式
    2、生产者消费者模式:
        1、特点:
            1、高内聚:在同一模块内,实现单一功能,尽量不使功能混杂
            2、低耦合:不同的模块之间尽量相互独立,减少模块间的影响
    3、代码实现

from threading import Thread 
import queue
import time

q = queue.Queue()#创建一个队列模型作为商品的仓库

class Producer(Thread):
    def run(self):
        count = 0
        while True:
            if q.qsize()<50:
                for i in range(3):
                    count +=1
                    msg = '产品 %d'%count
                    q.put(msg)#将产品放入仓库
            time.sleep(1)

class Customer(Thread):
    def run(self):
        while True:
            if q.qsize()>20:
                for i in range(2):
                    msg = q.get()#从仓库取出商品
                    print('消费了一个%s'%msg)
            time.sleep(1)
#创建三个生产者
for i in range(3):
    p = Producer()
    p.start()
#创建5个消费者
for i in range(5):
    c = Customer()
    c.start()
View Code

总结:
    1、进程和线程的区别
    2、会创建使用线程threading
    3、掌握基本的线程间同步互斥编程方法
    4、知道什么是GIL
    5、了解设计模式的概念
面试问题:
    1、进程和线程的区别
    2、同步和互斥
    3、给一个具体的情况,问采用进程还是线程,为什么?
    4、你是怎么处理僵尸进程的
    5、怎么测试一个硬盘的读写速度
    6、xxx框架,是用的多进程还是多线程
    7、进程间的通信方式

 

posted @ 2018-07-26 22:32  xdl_smile  阅读(313)  评论(0编辑  收藏  举报