前情回顾

1. 进程对象属性
   p.pid   p.name  p.is_alive()  p.daemon

2. 自定义进程类
   继承Process  重写run

3. 进程池   大量进程事件需要频繁创建删除进程
           Pool()
    apply_async()
    close()
    join()
    map()

4.进程间通信
  管道   消息队列   共享内存   信号  信号量  套接字
 
  管道: Pipe()    fd.recv()   fd.send()
  消息队列: Queue()  q.get()  q.put() q.full()
             q.empty()  q.qsize()  q.close()
  共享内存: Value()   Array()

  信号 :  kill  -l   
           kill  -sig  PID
    kill(pid,sig)
*****************************************************

import signal

signal.alarm(sec)
功能 : 向自身发送时钟信号 --》 SIGALRM
参数 : sec  时钟时间

* 进程中只能有一个时钟,第二个会覆盖第一个时间

同步执行 : 按照顺序逐句执行,一步完成再做下一步
异步执行 : 在执行过程中利用内核记录延迟发生或者准备             处理的事件。这样不影响应用层的持续执行。             当事件发生时再由内核告知应用层处理

* 信号是唯一的异步通信方法

signal.pause()
功能:阻塞等待接收一个信号

signal.signal(signum,handler)
功能: 处理信号
参数: signum  要处理的信号
       handler 信号的处理方法
           SIG_DFL  表示使用默认的方法处理
    SIG_IGN  表示忽略这个信号
    func     传入一个函数表示用指定函数处理
  def func(sig,frame)
      sig: 捕获到的信号
      frame : 信号对象

信号量(信号灯)

原理 : 给定一个数量,对多个进程可见,且多个进程都可以操作。进程通过对数量多少的判断执行各自的行为。

multiprocessing --》 Semaphore()

sem = Semaphore(num)
功能: 创建信号量
参数: 信号量初始值
返回: 信号量对象

sem.get_value()  获取信号量值
sem.acquire() 将信号量减1  当信号量为0会阻塞
sem.release() 将信号量加1

进程的同步互斥

临界资源 :多个进程或者线程都能够操作的共享资源
临界区 : 操作临界资源的代码段

同步 : 同步是一种合作关系,为完成某个任务,多进程或者多线程之间形成一种协调,按照约定或条件执行操作临界资源。

互斥 : 互斥是一种制约关系,当一个进程或者线程使用临界资源时进行上锁处理,当另一个进程使用时会阻塞等待,直到解锁后才能继续使用。

Event  事件

multiprocessing  --》 Event

创建事件对象
e = Event()

设置事件阻塞
e.wait([timeout])

事件设置 当事件被设置后e.wait()不再阻塞
e.set()

清除设置 当事件设置被clear后 e.wait又会阻塞
e.clear()

事件状态判断
e.is_set()

 

Lock 锁

创建对象
lock = Lock()

lock.acquire() 上锁  如果锁已经是上锁状态调用此函数会阻塞

lock.release() 解锁

with lock:   上锁
   ....
   ....
              解锁

线程

什么是线程

线程也是一种多任务编程方法,可以利用计算机多核资源完成程序的并发执行。线程又被称为轻量级的进程。

线程特征
* 线程计算机多核分配的最小单位
* 一个进程可以包含多个线程
* 线程也是一个运行的过程,消耗计算机资源,多个线程共享进程的资源和空间
* 线程的创建删除消耗的资源都要远远小于进程
* 多个线程之间执行互不干扰
* 线程也有自己的特有属性,比如指令集 ID

threading 模块创建线程

threading.Thread()
功能 : 创建线程对象
参数 :name  线程名称  默认 Thread-1
       target  线程函数
       args  元组   给线程函数位置传参
       kwargs  字典  给线程函数键值传参
 
t.start()  启动线程 自动运行线程函数
t.join([timeout])  回收线程


线程对象属性

t.is_alive()  查看线程状态
t.name  线程名称
t.setName()  设置线程名称
t.getName()  获取线程名称
threading.currentThread()  获取当前线程对象

t.daemon 属性
默认情况主线程退出不会影响分支线程执行
如果设置为True 则分支线程随主线程退出

设置方法:
t.daenon = True
t.setDaemon(True)

判断属性值
t.isDaemon()

*Daemon 要在start前设置,不会和join同用

创建自己的线程类
步骤:
1.继承Thread
2.加载Thread中的__init__
3.重写run方法


线程通信
   
    通信方法: 多个线程共享进程的空间,所以线程间通            信使用全局变量完成。

    注意事项: 线程间使用全局变量往往要同步互斥机制            保证通信安全

线程同步互斥方法

线程的event
e = threading.Event()  创建事件对象
e.wait([timeout])  如果e为设置状态则不阻塞否则阻塞
e.set()  将e变为设置状态
e.clear()  清除设置
e.is_set() 判断当前状态

线程锁
lock = threading.Lock()  创建锁对象
lock.acquire()  上锁
lock.release()  解锁

* 也可以通过with上锁,上锁状态调用acquire会阻塞

python线程的GIL问题 (全局解释器锁)

python--》支持多线程--》同步互斥--》加锁--》
超级锁,给解释器加锁--》解释器同一时刻只能解释一个线程

后果 : 一个解释器同一时刻只能解释执行一个线程,所以导致python线程效率低下。但是当遇到IO阻塞时线程会主动让出解释器,因此python线程更加适合高延迟的IO程序并发。

解决方法:
* 尽量用进程完成并发
* 不适用c解释器  c#  java
* 尽量使用多种方案组合的方式进行并发操作,线程用作高延迟IO

效率测试
Line cpu: 9.014907121658325
Line IO: 4.548823118209839

thread cpu: 9.38966417312622
thread  IO: 4.6143529415130615

Process  cpu: 5.466824531555176
Process  IO: 2.9468178749084473