40 day of python

线程

  守护线程

    在主线程结束(包括所有的子线程)之后守护线程才结束

  面向对象开启线程

    原本写在func中的代码挪到run方法中

  同步的机制

    锁

      GIL锁    是全局解释器锁 线程没有并行

        是Cpython解释器的

        在开发python解释器的时候可以减少很多细粒度的锁

      为什么有了GIL锁还会产生数据安全问题呢?

        因为GIL锁的是线程  而不是具体的内存

      互斥锁 不能连续acquire两次

      递归锁   可以在用一个线程/进程中被acquire多次

      死锁 : 是一种现象,而不是一个工具

        为什么产生死锁 : 代码的逻辑有问题

        如何解决死锁 : 

          如果在服务阶段 --->  递归锁  --->   排查逻辑  --->  互斥锁

          如果在测试阶段   --->   --->   排查逻辑   --->   互斥锁

    信号量    锁+计数器

      和池的区别:信号量是有几个任务起几个进程/线程  池是固定的线程/进程数,不限量的任务

      现象:信号量慢  且耗资源  池快

    事件  Event wait set clear is_set

    条件  condition  wait  notify  release

    定时器  Timer

join    同步控制  用来获取结果

锁      数据安全

池  提高效率,解决并发

 

今日内容

线程

  队列

  线程池

协程

IO模型

from multiprocessing import Queue,JoinableQueue  #  进程IPC队列
from queue import Queue         # 线程队列    先进先出
from queue import LifoQueue     # 后进后出的
put   get   put_nowait    get_nowait    full    empty    qsize
队列Queue
    先进先出
    自带锁  数据安全
栈 LifoQueue
    后进后出
    自带锁  数据安全
from multiprocessing import Queue,JoinableQueue
from queue import Queue
from queue import LifoQueue
lq = LifoQueue(4)
lq.put(123)
lq.put(456)
lq.put('abc')
lq.put('abc')
# lq.put('abc')
# lq.put('abc')
# lq.put('abc')
print(lq)
print(lq.get())
print(lq.get())
print(lq.get())
print(lq.get())
from queue import PriorityQueue  #  优先级队列
pq = PriorityQueue()
pq.put((10,'aaa'))
pq.put((5,'bbb'))
pq.put((100,'ccc'))
pq.put((20,'ddd'))
print(pq.get())
print(pq.get())
print(pq.get())
print(pq.get())

 

Threading  没有线程池的

Multiprocessing  pool

concurrent.futures帮助你管理线程池和进程池

import time
from threading import currentThread,get_ident
from concurrent.futures import ThreadPoolExecutor   #  帮助你启动线程池的类
from concurrent.futures import ProcessPoolExecutor   #  帮助你启动进程池的类

def func(i):
    time.sleep(1)
    print('in %s %s'%(i,currentThread()))
    return i**2

def back(fn):
    print(fn.result(),currentThread())

# map启动多线程任务
t = ThreadPoolExecutor(5)
t,map(func,range(20))
for i in range(20):
    t.submit(func,i)

# submit异步提交任务
t = ThreadPoolExecutor(5)
for i in range(20):
    t.submit(func,i)
t.shutdown()
print('main : ',currentThread())
# 起多少个线程池
    # 5*cpu的个数
# 获取任务结果
t = ThreadPoolExecutor(20)
ret_l = []
for i in range(20):
    ret = t.submit(func,i)
    ret_l.append(ret)
t.shutdown()
for ret in ret_l:
    print(ret.result())
print('main : ',currentThread())

 

回调函数

from threading import currentThread,get_ident
from concurrent.futures import ProcessPoolExecutor
def func(i):
    time.sleep(1)
    print('in %s %s'%(i,currentThread()))
    return i**2

def back(fn):
    print(fn.result(),currentThread())
if __name__ == '__main__':
    t = ProcessPoolExecutor(20)
    for i in range(100):
        t.submit(func,i).add_done_callback(back)

 

回调函数(进程版)

import os
import time
from concurrent.futures import ProcessPoolExecutor  # 帮助你启动线程池的类

def func(i):
    time.sleep(1)
    print('in %s %s'%(i,os.getpid()))
    return i**2

def back(fn):
    print(fn.result(),os.getpid())
if __name__ == '__main__':
    print('main : ',os.getpid())
    t = ProcessPoolExecutor(20)
    for i in range(100):
        t.submit(func,i).add_done_callback(back)

 

multiprocessing模块自带进程池的

threading模块是没有进程池的

concurrent.futures  进程池  和  线程池

  高度封装

  进程池/线程池的统一的使用方式

创建线程池/进程池  ProcessPoolExecutor  ThreadPoolExecutor

 

ret = t.submit(func,arg1,arg2....)  异步提交任务

ret.result()  获取结果,如果要想实现异步效果,应该使用列表

map(func,iterable)

shutdown  close+join  同步控制的

add_done_callback  回调函数,在回调函数内接收的参数是一个对象,需要通过result来获取返回值

  回调函数仍然在主进程中执行

 

进程:资源分配的最小单位,班级

线程:CPU调度最小单位,人

什么是协程?一个人分八半儿

CPython线程不能利用多核的

多线程凭什么能够做到并发???

多个线程无法利用多核

 

一个线程

  能同时执行多个任务

 

协程:能在一条线程的基础上,在多个任务之间互相切换

节省了线程开启的消耗

是从python代码的级别调度的

  正常的线程是CPU调度的最小单位

  协程的调度并不是由操作系统来完成的

 

def func():
    print(1)
    x = yield 'aaa'
    print(x)
    yield 'bbb'

g = func()
print(next(g))
print(g.send('****'))

 

在多个函数之间互相切换的功能  -   协程

def consumer():
    while True:
        x = yield 
        print(x)
def producer():
    g = consumer()
    next(g)     # 预激
    for i in range(10):
        g.send(i)
producer()

yeild  只能程序之间的切换,没有重利用任何IO操作的时间

 

程序执行的上下文切换的工具

greenlet  程序上下文切换的

import time
from greenlet import greenlet
# 协程模块  # 单纯的程序切换耗费时间
def eat():
    print('')
    time.sleep(1)
    g2.switch()         # 切换
    print('吃完了')
    time.sleep(1)
    g2.switch()

def play():
    print('玩儿')
    time.sleep(1)
    g1.switch()
    print('玩儿美了')
    time.sleep(1)

g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()             # 切换

遇到IO就切换

gevent  pip3 install gevent

greenlet是gevent的底层

gevent是基于greenlet实现的

python代码在控制程序的切换

 

 

使用协程减少IO操作带来的时间消耗

from gevent import monkey;monkey.patch_all()
import gevent
import time

def eat():
    print('')
    time.sleep(2)
    print('吃完了')

def play():
    print('玩儿')
    time.sleep(1)
    print('玩儿美了')

g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1,g2])

g1.join()

g2.join()

没执行

为什么没执行???是需要开启么?

没有开启但是切换了

  gevent帮你做了切换,做切换是有条件的,遇到IO才切换

  gevent不认识除了gevent这个模块内以外的IO操作

  使用join可以一直阻塞直到协程任务完成

帮助gevent来认识其他模块中的阻塞

  from gevent import monkey;monkey.patch_all()写在其他模块导入之前

 

协程完成的socket

4c并发5000 qps

5个进程

20个线程

500个协程

 

协程  能够在单核的情况下  极大地提高CPU的利用率

  不存在数据不安全

  也不存在线程切换\创造的时间开销

  切换是用户级别的,程序不会因为协程中某一个任务进入阻塞状态而使整条线程阻塞

线程的切换

  时间片到了   降低CPU的效率

  IO会切         提高CPU效率

from gevent import monkey;monkey.patch_all()
import socket
import gevent
def talk(conn):
    while True:
        conn.send(b'hello')
        print(conn.recv(1024))
sk = socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)
服务器
import socket
from threading import Thread
def client():
    sk = socket.socket()
    sk.connect(('127.0.0.1',9090))
    while True:
        print(sk.recv(1024))
        sk.send(b'bye')

for i in range(500):
    Thread(target=client).start()
客户端
from threading import Condition
acquire
release
wait    阻塞
notify  让wait解除阻塞的工具
wait还是notify在执行这两个方法的前后 必须执行acquire和release
from threading import Condition,Thread
def func(con,i):
    con.acquire()
    # 判断某条件
    con.wait()
    print('threading : ',i)
    con.release()

con = Condition()
for i in range(20):
    Thread(target=func,args=(con,i)).start()
con.acquire()
# 帮助wait的子线程处理某个数据直到满足条件
con.notify_all()
con.release()
while True:
    num = int(input('num >>>'))
    con.acquire()
    con.notify(num)
    con.release()

 

posted @ 2018-07-31 20:37  贾迪123  阅读(96)  评论(0编辑  收藏  举报