python之路多进程和多线程总结(四)

2018-9-23 15:57:51

终于把多进程和多线程东西整理完了!

下次整理那个数据库->前端->Django

慢慢来! 发现学了好多东西!

 

1. 僵尸进程,孤儿进程
1.1 僵尸进程:子进程结束,父类未结束
1.2 孤儿进程:父类进程over.,子进程未结束

'''
多线程的学习
'''
#使用线程的第一种方式,直接调用threading里面的Thread类
from threading import Thread
import time

def test():
    print("--====昨晚喝多了,下次少喝点!!!")
    time.sleep(1)

def main():
    '''创建线程'''
    for i in range(5):
        #创建一个线程
        t = Thread(target=test)
        t.start()

if __name__ == '__main__':
    main()


#使用线程的第二种方式,封装成一个类,
import threading
import time

class MyThread1(threading.Thread):
    """创建一个类继承多线程类"""
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm" +slef.name+'@'+str(1) #name属性中保存的是当前线程的名字
            print(msg)

if __name__ == '__main__':
    t = MyThread1()
    t.start()

2. 线程和进程区别
进程之间不能共享全局变量
线程之间共享全局变量,线程函数中的局部变量不是共享的!!

from threading import Thread
import time 

g_num = 100
def  work1():
    global g_num
    for i in range(3):
        g_num += 1
    print("-----in work1,g_num is %d-----"%g_num)

def work2():
    global g_num
    print("-------in work2 ,g_num is %d-----"%g_num)


print("---------线程创建之前g_num is %d---"%g_num)
t1 = Thread(target=work1)
t1.start()
#延时一会,保证t1线程中的事情做完
time.sleep(1)
t2 = Thread(target=work2)
t2.start()

3. 互斥锁
关于互斥锁,解决线程占用的问题!!!
上锁的代码越少越好!该加的时候才加!

#--*utf- 8*--
from threading import Thread ,Lock
import time 

g_num = 0

def  work1():
    global g_num
    #上锁, 这个线程和work2线程都在抢着 对这个锁进行上锁,如果由1方成功的上锁
    #那么导致一方会堵塞(一直等待)到这个锁被解开位置
    mutex.acquire()
    for i in range(100000):
        g_num += 1
    #解锁
    #用来对mutex指向的这个锁, 进行解锁,,只要开了锁,那么接下来会让所有
    #因为这个锁 被锁上的锁 而堵塞的线程,进行抢着上锁
    mutex.release()
    print("-----in work1,g_num is %d-----"%g_num)

def work2():
    global g_num
    #上锁
    mutex.acquire()
    for i in range(100000):
        g_num += 1
    #解锁
    mutex.release()
    print("-------in work2 ,g_num is %d-----"%g_num)


#创建一把互斥锁,这个锁默认是没有上锁的
mutex =Lock()
def main():
    t1 = Thread(target=work1)
    t1.start()
    #延时一会,保证t1线程中的事情做完
    # time.sleep(1) #取消屏蔽之后,再次运行程序
    t2 = Thread(target=work2)
    t2.start()

if __name__ == '__main__':
    main()

4. 同步应用:
同步的应用!就是一个接着一个

#同步的应用!
from threading import Thread,Lock
from time import sleep

class Task1(Thread):
    def  run(self):
        while True :
            if lock1.acquire():
                print("------Task1----------")
                sleep(0.5)
                #给Task2开锁!
                lock2.release()


class Task2(Thread):
    def run(self):
        while True:
            if lock2.acquire():
                print("------Task2----------")
                sleep(0.5)
                #给Task3开锁
                lock3.release()


class Task3(Thread):
    def run(self):
        while True:
            if lock3.acquire():
                print("------Task3----------")
                sleep(0.5)
                lock1.release()

#使用Lock创建出的锁默认没有"锁上"
lock1 = Lock()
#创建另外一个锁,并且锁上
lock2 = Lock()
lock2.acquire()
#创建另外一个锁,并且"锁上"
lock3 = Lock()
lock3.acquire()

t1 = Task1()
t2 = Task2()
t3 = Task3()

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

5.降低代码的耦合性

#解决耦合的问题
#用队列解决这种问题,起到了缓冲的作用
import threading
import time
# #python2中
# from Queue import Queue

#python3中
from queue import Queue

class Producer(threading.Thread):
    def run(self):
        global queue
        count = 0
        while True:
            if queue.qsize() < 1000:
                for i in range(100):
                    count = count +1
                    msg = '生成产品'+str(count)
                    queue.put(msg)
                    print(msg)
            time.sleep(0.5)


class Consumer(threading.Thread):
    def run(self):
        global queue
        while True:
            if queue.qsize() > 100:
                for i in range(3):
                    msg = self.name + '消费了 '+queue.get()
                    print(msg)
            time.sleep(1)


if __name__ == '__main__':
    queue = Queue()
    for i in range(500):
        queue.put('初始产品'+str(i))
    for i in range(2):
        p = Producer()
        p.start()
    for i in range(5):
        c = Consumer()
        c.start()

反思: 多看别人的代码,多问自己是否能写出来这样优雅代码!  代码一定要低耦合,高优雅!!!敲代码要多思考!

6.python中的GIL,线程锁
在python中,多进程效率远大于多线程效率
python中存在GIL这个"线程锁",
关键地方可以使用c语言解决 GIL问题 然后可以提高cpu占用效率

 

7.异步的实现!
同步调用就是你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去
异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了。

from multiprocessing import Pool
import time
import os

def test():
    print("---进程池中的进程---pid=%d,ppid=%d--"%(os.getpid(),os.getppid()))
    for i in range(3):
        print("----%d---"%i)
        time.sleep(1)
    return "hahah"

def test2(args):
    print("---callback func--pid=%d"%os.getpid())
    print("---callback func--args=%s"%args)

pool = Pool(3)
pool.apply_async(func=test,callback=test2)
time.sleep(5)
print("----主进程-pid=%d----"%os.getpid())

8.反射

    # 通过反射
    # 对象名 获取对象属性 和普通方法
    # 类名  获取静态属性 和类方法 和静态方法
    # 普通方法 self
    # 静态方法 @staticmethod
    # 类方法  @classmethod
    # 属性方法@ property
    # 继承
    # 封装
    # 反射,, 应用:从类里面获取字符串
    # 在python中万物皆对象
class Teacher:
    dic = {"查看学生信息":"show_student","查看讲师信息":"show_teacher"}
    def  show_student():
        print("show_student")
 
    def show_teacher():
        print("show_teacher")
    if hasattr(Teacher,"dic"):
        #如果类中有dic 则直接调用
        ret = getattr(Teacher,"dic")  #Teacher.dic  #类也是对象
# print(ret)
alex = Teacher()
for k in Teacher.dic:
    print(k)
key = input("输入需求:")
func = getattr(alex,Teacher.dic[key])
func()

9.一个_rcv面试题

#类调用属性,属性没有,用__getatrr__魔法方法!
#目的打印出 think different itcast

class Foo(object):
    def  __init__(self):
        pass

    def __getattr__(self,item):
        print(item, end="")
        return self
    def __str__(self):
        return ""

print(Foo().think.different.itcast)

10.python多线程和多进程详细总结
# python多进程
#!@Author   TrueNewBee

import time
import os
from multiprocessing import Process


def func(args):
    print(args)
    print(54321)
    time.sleep(1)
    print("子进程:",os.getpid())
    print('子进程的父进程:', os.getppid())
    print(12345)


if __name__ =="__main__":
    #如果函数需要传参,就可以如下,args=() <--这是一个元组一个参数得加,
    #函数名不加括号
    p = Process(target=func,args=(54321,)) #注册
    #p是一个进程对象,还没有启动进程
    #异步(不是同时进行的)
    p.start()       #开启了一个子进程
    print('*'*10)
    print('父进程:',os.getpid()) #查看当前进程号
    print('父进程的父进程:', os.getppid()) #其实就是pycharm进程数

#进程的生命周期
    #主进程  从运行到运行结束
    #子进程  从start()开始,函数运行完结束
    #开启了子进程的主进程:
        #自己的代码如果长,等待自己的代码执行结束才结束,
        #子进程的执行时间长,主进程会在主进程代码执行完毕后等待子进程执行完毕后 主进程结束

10.多进程异步

import  time
from multiprocessing import  Process

#多进程打印*   异步
def func(filename,content):
    """定义一个函数执行打印"""
    print('*'*arg1)
    time.sleep(5)
    print('*'*arg2)


if __name__ =="__main__":
    # 异步,并不是按顺序同时打印
    # for i in  range(10):
    #     # p = Process(target=func,args=(10*i,20*i))
    #     # p.start()
    p_list = [] #创建一个进程对象的列表
    for i in range(10):
        p = Process(target=func,args=(10*i,20*i))
        p_list.append(p)
        p.start()
        #让"运行完了最后打印,是个进程都是异步,在此之前所有子进程全部进行完了,然后才是同步
    # for p in p_list:p.join()  列表推导式还原,其实就是一个for循环
    [p.join() for p in p_list]  #列表推导式
    print("运行完了")

import  time
from multiprocessing import  Process


def func(arg1,arg2):
    """定义一个函数执行打印"""
    print('*'*arg1)
    time.sleep(5)
    print('*'*arg2)

# 先打印了 "运行完了"  5秒 再打印 arg2  实现了异步效果
if __name__ =="__main__":
    p = Process(target=func ,args=(10,20))
    p.start()
    print("hhhhhhh") #join()和start()之间的部分仍然是异步,在join()下面才是同步
    # p.join()  #是感知一个子进程的结束,将异步的程序变为同步
    """实现了异步效果"""
    print('========:运行完了')

11.python进程锁(Demo)

#
# 火车票
import json
import time
from multiprocessing import Process
from multiprocessing import Lock

def show(i):
    with open('ticket') as f:
        dic = json.load(f)
    print('余票: %s'%dic['ticket'])

def buy_ticket(i,lock):
    lock.acquire() #拿钥匙进门
    with open('ticket') as f:
        dic = json.load(f)
        time.sleep(0.1)
    if dic['ticket'] > 0 :
        dic['ticket'] -= 1
        print('\033[32m%s买到票了\033[0m'%i)
    else:
        print('\033[31m%s没买到票\033[0m'%i)
    time.sleep(0.1)
    with open('ticket','w') as f:
        json.dump(dic,f)
    lock.release()      # 还钥匙

if __name__ == '__main__':
    for i in range(10):
        p = Process(target=show,args=(i,))
        p.start()
    lock = Lock()
    for i in range(10):
        p = Process(target=buy_ticket, args=(i,lock))
        p.start()

12.进程 信号量

回顾:
多进程代码
from multiprocessing import Process
方法
进程对象.start() 开启一个子进程
进程对象.join() 感知一个子进程的结束
进程对象.terminate() 结束一个子进程
进程对象.is_alive() 查看某个子进程是否还在运行
属性
进程对象.name 进程名
进程对象.pid 进程号
进程对象.daemob 值为True的时候,表现新的子进程是一个守护进程
守护进程 随着主进程代码的执行结束而结束
一定在start之前设置

rom  multiprocessing import Lock
l = Lock()
l.acquire()    #拿钥匙
# 会造成数据不安全的操作
l.release()    #换钥匙

# 信号量
# 多进程中的组件
# 一套资源  同一时间 只能被n个人访问
# 某一段代码 同一时间 只能被n个进程执行
import time
import random
from multiprocessing import Process
from multiprocessing import Semaphore


def ktv(arg, sem1):
    sem1.acquire()   # 获取钥匙
    """模拟唱歌"""
    print("%s走进ktv"%arg)
    time.sleep(random.randint(1, 5))
    print("%s走出ktv"%arg)
    sem.release()


if __name__ == '__main__':
    sem = Semaphore(4)
    for i in range(20):
        p = Process(target=ktv, args=(i, sem))
        p.start()

13.事件

事件:通过一个信号 来控制 多个进程 同时 执行或者阻塞
一个信号可以使所有的进程都进入阻塞状态
也可以控制所有进程的解除阻塞
一个事件被创建之后,默认是阻塞状态

set 和 clear
分别用来修改一个事件的状态 True或者False
is_set 用来查看一个事件的状态
wait 是根据事件的状态来解决自己是否在wait处阻塞
False阻塞 True不阻塞

from multiprocessing import Event


def event():
    e = Event()  # 创建一个事件
    print(e.is_set())  # 查看一个事件的状态,默认设置成阻塞
    e.set()  # 将这个事件的状态改为True
    print(e.is_set())
    e.wait()  # 是依据e.is_set()的值来决定是否阻塞
    print(123456)
    e.clear()  # 将这个事件的状态改为False
    print(e.is_set())
    e.wait()    # 等待事件的信号被编程True
    print('*'*10)


if __name__ == '__main__':
    event()


# 红绿灯事件
# 用事件控制信号,控制进程
import time
import random
from multiprocessing import Process
from multiprocessing import Event


def cars(e2, i1):
    """创建一个车"""
    if not e2.is_set():
        print("car%i1在等待" % i1)
        e2.wait()  # 阻塞,直到得到一个 事件状态变成True的信号
    print("car%s通过" % i1)


def light(e1):
    """灯是独立的进程"""
    while True:
        if e1.is_set():
            time.sleep(2)
            e1.clear()
            print("绿灯亮了")
        else:
            e1.set()
            print("红灯亮了")
        time.sleep(2)


if __name__ == '__main__':
    e = Event()
    traffic = Process(target=light, args=(e, ))
    traffic.start()
    for i in range(20):
        car = Process(target=cars, args=(e, i))
        car.start()
        time.sleep(random.random())

14.队列

import time
from multiprocessing import Queue


q = Queue(5)
q.put(1)    # 向队列里面放值
q.put(2)
q.put(3)
q.put(4)
q.put(5)
# 如果队列满了再添加则出现堵塞
print(q.full())  # 队列是否满了
print(q.get())  # 取出来数
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 如果队列全取出了再去将出现阻塞
print(q.empty())    # 判断队列是否为空
while True:
    """检查队列是否为空"""
    try:
        q.get_nowait()
    except :
        print("队列已空")
        time.sleep(1)

from multiprocessing import Process
from multiprocessing import Queue


# 队列生产和消费数据
def produce(q1):
    """队列存储数据"""
    q1.put('hello')


def consume(q2):
    """队列消费数据"""
    print(q2.get())


if __name__ == '__main__':
    q = Queue()
    p = Process(target=produce,args=(q, ))
    p.start()
    c = Process(target=consume, args=(q, ))
    c.start()

15.生产者和消费者 joinableQueue模型:

# 队列
# 生产者消费者模型  解决供需不平衡的问题
import time
import random
from multiprocessing import Process, JoinableQueue


def consumer(name, q1):
    """负责消费生产的东西"""
    while True:
        food = q1.get()
        if food is None:     # 判断为空则停止这个循环
            print('%s获取到一个空' % name)
            break
        print('\033[31m%s消费了%s\033[0m' % (name, food))
        time.sleep(random.randint(0, 2))
        q1.task_done()  # 提交回执 count - 1


def producer(name, food, q1):
    """负责生产包子"""
    for i in range(10):
        time.sleep(random.randint(0, 2))
        f = '%s生产了%s%s' % (name, food, i)
        print(f)
        q1.put(f)
    q1.join()   # 阻塞,直到一个队列中的数据 全部被执行完毕


if __name__ == '__main__':
    q = JoinableQueue(20)
    p1 = Process(target=producer, args=('Egon', '包子', q))
    p2 = Process(target=producer, args=('WuSir', '泔水', q))
    c1 = Process(target=consumer, args=('jinBoss', q))
    c2 = Process(target=consumer, args=('alex', q))
    p1.start()
    p2.start()
    c1.daemon = True  # 成为守护进程,主进程中的代码执行完毕之后,子进程自动结束
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()   # 感知一个进程结束
    p2.join()


# 在消费者这一端:
#     每次获取一个数据
#     处理一个数据
#     发送一个记号:标志一个数据被处理成功

# 在生产者这一端
#     每一次生产一个数据
#     且每一次生产的数据放在队列中
#     在队列中刻上一个记号
#     当生产者全部生产完毕后
#     join信号: 已经停止生产数据了
#     且要等待之前被刻上的记号都被消费完
#     当数据都被处理完时,join阻塞结束

# consumer 中把所有的任务消耗完
# produce 端的join感知到,停止阻塞
# 所有的producer进程结束
# 主进程中的p.join结束
# 守护进程(消费者进程)结束

16.生产者和消费者 Queue模型:

# 队列
# 生产者消费者模型  解决供需不平衡的问题
import time
import random
from multiprocessing import Queue, Process


def consumer(name, q1):
    """负责消费生产的东西"""
    while True:
        food = q1.get()
        if food is None:    # 判断为空则停止这个循环
            print('%s获取到一个空' % name)
            break
        print('\033[31m%s消费了%s\033[0m' % (name, food))
        time.sleep(random.randint(0, 2))


def producer(name, food, q1):
    """负责生产包子"""
    for i in range(10):
        time.sleep(random.randint(0, 2))
        f = '%s生产了%s%s' % (name, food, i)
        print(f)
        q1.put(f)


if __name__ == '__main__':
    q = Queue(20)
    p1 = Process(target=producer, args=('Egon', '包子', q))
    p2 = Process(target=producer, args=('WuSir', '泔水', q))
    c1 = Process(target=consumer, args=('jinBoss', q))
    c2 = Process(target=consumer, args=('alex', q))
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join()   # 感知一个进程结束
    p2.join()
    q.put(None)
    q.put(None)

17.复习总结内容:

复习:
信号量 Semaphore
from multiprocessing import Semaphore
用锁的原理实现的,内置了一个计数器
在同一时间,只能有指定数量的进程执行某一段被控制的代码

事件
wait 阻塞收到时间状态控制的同步组件
状态 True False is_set
true->false clear()
false->true set()
wait 状态为True不阻塞 状态为False的时候阻塞

上面都是为了同步!!!(让代码按顺序执行)

队列
Queue
put 当队列满的时候阻塞等待队列有空位置
get 当队列空的时候阻塞等待队列有数据
full empty 不完全准确

JoinableQueue
get task_done 这两个联用
put join 这两个联用

 

18. python进程间数据共享

# 管道
# 数据共享  Manager
# 进程池和回调函数 !
# 管道  实现两进程间的信息传递  
from multiprocessing import Pipe, Process


# 一种使用管道方式
def func(conn3, conn4):
    conn4.close()   # 把多余通道关闭
    while True:
        try:
            msg = conn3.recv()  # 接收消息
            print(msg)
        except EOFError:    # 没有数据可以取的时候抛出异常
            conn3.close()
            break


if __name__ == '__main__':
    conn1, conn2 = Pipe()  # 接收两个参数 两个管道
    Process(target=func, args=(conn1, conn2)).start()
    conn1.close()  # 关闭多余通道
    for i in range(20):
        conn2.send('吃了吗')   # 发送消息
    conn2.close()
# pipe 数据不安全性  这是一个例子,下面有另外一个解决方案的代码加上锁!
# IPC
import time
import random
from multiprocessing import Process, Pipe


def producer(con1, pro1, name, food):
    """通过管道把生产东西传给消费者"""
    con1.close()
    for i in range(4):
        time.sleep(random.randint(1, 3))
        f = '%s生产%s%s' % (name, food, i)
        print(f)
        pro1.send(f)
    pro1.close()


def consumer(con2, pro2, name1):
    pro2.close()
    while True:
        try:
            food = con2.recv()
            print('%s吃了%s' % (name1, food))
            time.sleep(random.randint(1, 3))
        except EOFError:
            con2.close()
            break


if __name__ == '__main__':
    con, pro = Pipe()
    p = Process(target=producer, args=(con, pro, 'a', '泔水'))
    c1 = Process(target=consumer, args=(con, pro, 'b'))
    c2 = Process(target=consumer, args=(con, pro, 'c'))
    c3 = Process(target=consumer, args=(con, pro, 'd'))
    p.start()
    c1.start()
    c2.start()
    c3.start()
    con.close()
    pro.close()
# 加上锁解决pipe数据不安全问题
from multiprocessing import Process, Pipe, Lock


def consumer(p, name, lock1):
    produce1, consume1 = p
    produce1.close()
    while True:
        lock1.acquire()
        food=consume1.recv()
        lock.release()
        if food:
            print('%s 收到包子:%s' %(name,baozi))
        else:
            consume1.close()
            break


def producer(p, n):
    produce2, consume2=p
    consume2.close()
    for i in range(n):
        produce2.send(i)
    produce2.send(None)
    produce2.send(None)
    produce2.close()


if __name__ == '__main__':
    produce,consume=Pipe()
    lock = Lock()
    c1 = Process(target=consumer, args=((produce, consume), 'c1', lock))
    c2 = Process(target=consumer, args=((produce, consume), 'c2', lock))
    p1 = Process(target=producer, args=((produce, consume), 10))
    c1.start()
    c2.start()
    p1.start()

    produce.close()
    consume.close()

    c1.join()
    c2.join()
    p1.join()
    print('主进程')

加锁来控制操作管道的行为 来避免进程之间争抢数据造成的数据不安全现象
队列 进程之间数据安全的
管道 + 锁

牺牲效率 保障了数据安全

from multiprocessing import Manager, Process, Lock


def main(dic1, lock1):
    lock1.acquire()  # 加上所也是为了数据不安全问题
    dic1['count'] -= 1
    lock1.release()


if __name__ == '__main__':
    m = Manager()
    lock = Lock()
    dic = m.dict({'count': 100})
    p_list = []
    for i in range(50):
        p = Process(target=main, args=(dic, lock))
        p.start()
        p_list.append(p)
    for i in p_list:
        p.join()
    print('主进程:', dic)

19. python进程池

主要方法  常用的就是   from multiprocessing import Pool
map() 同步
apply() 同步
apply_async()  异步  手动 close()  join()   学的逐渐的由浅入深

为什么会有进程池的概念
  效率
  每开启进程,开启属于这个进程的内存空间
寄存器 堆栈 文件
进程过多 操作系统的调度

进程池
  python中的 先创建一个属于进程的池子
  这个池子指定能放多少个进程
  先将这些进程创建好

更高级的进程池
  可以根据用户需求改变进程数量
    自带join方法,里面是异步
    map(func,range) 里面传入的参数只能传入可迭代的  range ,列表,字典等
import time
from multiprocessing import Pool, Process


def func(n):
    for a in range(10):
        print(n+1)


if __name__ == '__main__':
    start = time.time()
    pool = Pool(5)                  # 5个进程
    pool.map(func, range(100))      # 100个任务  异步
    t1 = time.time() - start
    p_list = []
    for i in range(100):
        p = Process(target=func, args=(i, ))
        p_list.append(p)
        p.start()
    for i in p_list:p.join()
    t2 = time.time()-start
    print(t1, t2)


# apply() 方法为同步的
# apply_async() 方法为异步的 一般都是用这个
import time
import os
from multiprocessing import Pool


def func(n):
    print('start func%s' % n, os.getpid())
    time.sleep(1)
    print('end func%s' % n, os.getpid())


if __name__ == '__main__':
    p = Pool(5)
    for i in range(10):
        # p.apply(func, args=(i, ))     # 该方法为同步的
        p.apply_async(func, args=(i,))  # 该方法为异步的
    # 使用apply_async 必须加的两句话
    p.close()   # 结束进程接收任务
    p.join()    # 感知进程池中的任务执行结束


# p = Pool()
# p.map(funcName, iterable)     默认异步的执行任务,且自带close和join
# p.apply 同步调用的
# p.apply_async 异步调用 和主进程完全异步 需要手动close 和join
import time
from multiprocessing import Pool


def func(i1):
    time.sleep(0.5)
    return i1*i1


if __name__ == '__main__':
    p = Pool()
    # res_list = []   # 储存res对象 到后面一块被接收
    # for i in range(10):
    #     res = p.apply_async(func, args=(i, ))   # apply_async的结果就是func的返回值
    #     res_list.append(res)
    #     # res.get()    # get() 等着func的计算结果,阻塞了(同步)
    # for res in res_list:
    #     print(res.get())   # 每五个返回一次数据 让get()变成了异步

    # map()
    ret = p.map(func, range(100))
    print(ret)  # 整体返回所有数据

20. 回调函数

import os
from multiprocessing import Pool


def func1(n):
    print('in func1',os.getpid())
    return n*n


def func2(nn):
    print('in func2', os.getpid())
    print(nn)


if __name__ == '__main__':
    print('主进程:', os.getpid())  # 回调函数在主进程中执行的
    p = Pool(5)
    for i in range(10):
        p.apply_async(func1, args=(10, ), callback=func2)  # 回调func2返回值编程参数传给func1
    p.close()
    p.join()

21.进程池方式的 socket

server端

# 用进程池子方法让服务端接就收多个客户端发来消息
# apply_async() 异步方法
import socket
from multiprocessing import Pool


def func(conn1):
    conn1.send(b'hello')
    print(conn.recv(1024).decode('utf-8'))
    conn1.close()


if __name__ == '__main__':
    p = Pool(5)
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen()
    while True:
        # 需要异步 多进程就收客户端发来消息
        conn, add = sk.accept()
        p.apply_async(func, args=(conn, ))
    sk.close()

client 端

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8080))
ret = sk.recv(1024).decode('utf-8')
print(ret)
msg = input('>>>').encode('utf-8')
sk.send(msg)
sk.close()

22. 上述复习和总结:

22.1:
        进程池所有内容:
        管道
        数据的共享 Manager dic list
        进程池
          cpu个数+1
          ret = map(func, iterable)
              异步 自带close和join
              所有结果的[]
          apply()
              同步的 : 只有当func执行完之后,才会继续向下执行其他的代码
              apply(func, args=())
              返回值就是func的return
          apply_async
              异步的:当func被注册进入一个进程之后,程序就继续向下执行
              apply_async(func, args())
              返回值: apply_async返回的对象
                  为了用户能从中获取func的返回对象 obj.get()
              get会阻塞直到对应的func执行完毕拿到结果
              使用apply_async给进程池分配任务,需要线close()后join来保持多进程和主进程代码的同步性
  22.2:回调函数

                # 回调函数
                from multiprocessing import Pool


                def func1(n):
                    return n+1


                def func2(m):
                    print(m)


                if __name__ == '__main__':
                    p = Pool(5)
                    for i in range(10, 20):
                        p.apply_async(func1, args=(i, ), callback=func2)
                    p.close()
                    p.join()

22.3线程:

import os
import time
from threading import Thread
"""多线程并发,都在同一个进程运行"""

def func(n):
    time.sleep(1)   # 全部线程并发睡1s 然后打印
    print(n, os.getpid())


print('主线程:', os.getpid())
for i in range(10):
    t = Thread(target=func, args=(i, ))
    t.start()
"""使用面向对象方式开启线程"""
class MyTread(Thread):
    def __init__(self, arg):
        super().__init__()
        self.arg = arg

    def run(self):
        time.sleep(1)
        print(self.arg)

t = MyTread(10)
t.start()    

threading模块:
multiprocessing模块的完全模仿threading模块的接口,
二者在使用层面上有很大的相似地方
修改全局变量
在同一个进程多个线程之间的数据是共享的

def func1(a):
    global g
    g = 0
    print(g, a, os.getpid())

g = 100
t_list = []
for i in range(10):
    t = Thread(target=func1, args=(i, ))
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()
print(g)

进程 是最小的内存分配单位
线程 是操作系统调度的最小单位
线程被cpu执行了
进程内至少含有一个线程
进程中可以开启多个线程
开启一个线程所需要的时间要远远小于开启一个进程
多个线程内部有自己的数据栈,数据不共享
全局变量在多个线程之间是共享的
在CPython解释器下的python程序 在同一时刻 多线程只能有一个线程cpu被执行
高CPU(用多进程处理): 计算类 -----高CPU利用率 不占优势
高IO(用多线程处理): 爬取网页 200个网页
qq聊天 send recv
处理日志文件 读文件
处理web请求
读取数据库 写数据库

import time
from threading import Thread
from multiprocessing import Process
"""多线程与多进程时间对比"""

def func(n):
    n+1


if __name__ == '__main__':
    start = time.time()
    t_list = []
    for i in range(100):
        t = Thread(target=func, args=(i, ))
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    t1 = time.time() - start

    start1 = time.time()
    p_list = []
    for i in range(100):
        p = Process(target=func, args=(i, ))
        p.start()
        p_list.append(t)
    for p in p_list:
        p.join()
    t2 = time.time() - start1
    print(t1, t2)

     22.4线程模块中其他方法

import threading
import time


def func(n):
    time.sleep(0.5)
    # 查看线程名字和id
    print(n, threading.current_thread(), threading.get_ident())


for i in range(10):
    threading.Thread(target=func, args=(i, )).start()
print(threading.current_thread())
print(threading.active_count())  # 查看所有线程数  11 加上主线程
print(threading.enumerate())

23. 多线程写 socket sever 

sever端

import socket
from threading import Thread


def chat(conn1):
    conn1.send(b'hello')
    msg = conn1.recv(1024).decode('utf-8')
    print(msg)
    inp = input(">>").encode('utf-8')
    conn1.send(inp)
    conn1.close()


if __name__ == '__main__':
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen()
    while True:
        conn, add = sk.accept()
        # 创建一个多线程实现多线程通讯
        Thread(target=chat, args=(conn, )).start()
    sk.close()

client端  (多线程中可以用input  而多进程中不可以用input)

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8080))

msg = sk.recv(1024)
print(msg)
inp = input('>>>>').encode('utf-8')  # 多线程可以用input,多进程不可以用
sk.send(inp)
sk.close()

25. 还是复习和总结:

25.1.复习:

正确的学习方法
input
output
correct 纠正

线程:
1.线程是进程中的执行单位
2.线程是cpu执行的最小单位
3.线程之间资源共享
4.线程的开启和关闭以及切换的时间开销远远小于进程
5.线程本身可以在同一时间使用多个cpu
python 与 线程
CPython解释器在解释代码过程中容易产生数据不安全的问题
GIL全局解释器锁 锁的是线程
threading

25.2.守护线程:

import time
from threading import Thread


def func1():
    while True:
        print('*'*10)
        time.sleep(1)


def func2():
    print('in func2')
    time.sleep(5)


if __name__ == '__main__':
    t = Thread(target=func1, )
    t.daemon = True
    t.start()
    t2 = Thread(target=func2, )
    t2.start()
    t2.join()
    print('主线程')

# 守护进程随着主进程代码的结束而结束(进程间资源不共享,所以想咋结束咋结束)
# 守护线程会在主线程结束之后等待其他子线程的结束才结束(线程间资源共享,所以不能主线程不能立马结束)

# 主进程在执行完自己的代码之后不会立即结束,而是等待子进程结束之后 挥手子进程的资源

# import time
# from multiprocessing import Process


# def func():
#     time.sleep(5)


# if __name__ == '__main__':
#     Process(target=func, ).start()    

25.3.锁

# import time
# from threading import Thread, Lock


# def func(lock1):
#     global n
#     lock1.acquire()  # 加上一个锁
#     # n = 1  python内部就是下面执行的
#     temp = n
#     time.sleep(0.2)
#     n = temp - 1    # 9 刚取回来还没来得及赋值又被别人拿走了,所以得自己加个锁不让被人拿走
#     lock1.release()  # 换钥匙
#
#
# n = 10
# t_list = []
# lock = Lock()
# for i in range(10):
#     t = Thread(target=func, args=(lock, ))
#     t.start()
#     t_list.append(t)
# for t in t_list:
#     t.join()
# print(n)    # 不加锁是9 加锁是 0

# 科学家吃面问题 经典死锁问题
# noodle_lock = Lock()
# fork_lock = Lock()
# 互斥锁
#
#
# def eat1(name):
#     noodle_lock.acquire()
#     print('%s拿到面条啦' % name)
#     fork_lock.acquire()
#     print('%s拿到叉子啦' % name)
#     print('%s吃面' % name)
#     fork_lock.release()
#     noodle_lock.release()
#
#
# def eat2(name):
#     fork_lock.acquire()
#     print('%s拿到叉子啦' % name)
#     time.sleep(1)
#     noodle_lock.acquire()
#     print('%s拿到面条啦' % name)
#     print('吃面')
#     noodle_lock.release()
#     fork_lock.release()
#
#
# if __name__ == '__main__':
#     Thread(target=eat1, args=('alex', )).start()
#     Thread(target=eat2, args=('Egon',)).start()
#     Thread(target=eat1, args=('bossJin',)).start()
#     Thread(target=eat2, args=('zeZha',)).start()
import time
from threading import RLock, Thread


fork_lock = noodle_lock = RLock()   # 一个钥匙串上的两把钥匙
# 递归锁   为了解决死锁问题,可以acquire()多次,


def eat1(name):
    noodle_lock.acquire()   # 一把钥匙
    print('%s拿到面条啦' % name)
    fork_lock.acquire()
    print('%s拿到叉子啦' % name)
    print('%s吃面' % name)
    fork_lock.release()
    noodle_lock.release()


def eat2(name):
    fork_lock.acquire()
    print('%s拿到叉子啦' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s拿到面条啦' % name)
    print('%s吃面' % name)
    noodle_lock.release()
    fork_lock.release()


if __name__ == '__main__':
    Thread(target=eat1, args=('alex', )).start()
    Thread(target=eat2, args=('Egon',)).start()
    Thread(target=eat1, args=('bossJin',)).start()
    Thread(target=eat2, args=('zeZha',)).start()

25.4.条件和定时器

import time
from threading import Semaphore, Thread


def func(sem1, a, b):
    # 同一时间就让四个线程执行代码
    sem1.acquire()
    time.sleep(1)
    print(a+b)
    sem1.release()


if __name__ == '__main__':
    sem = Semaphore(4)
    for i in range(10):
        t = Thread(target=func, args=(sem, i, i+5))
        t.start()

25.5.事件

# 事件被创建的时候
# False状态
#       wait() 阻塞
# True状态
#       wait() 非阻塞
# clear 设置状态为False
# set   设置状态为True


# 数据库- 文件夹
# 文件夹里有好多excel表格
#   1.能够更方便的对数据进行增删改查
#   2.安全访问的机制


# 伪代码 看现象:
# 起两个线程
# 第一个线程:连接数据库
#   等待一个信号,告诉我我们之间的网络是通的
#   连接数据库
# 第二个线程:检测与数据库之间的网络情况是否连通
#       time.sleep(0,2)
#       将事件状态设置为True

import time
import random
from threading import Thread, Event


def connect_db(e1):
    count = 0
    while count < 3:
        # 连接三次
        e1.wait(0.5)  # 状态是False的时候,我只等待1s
        if e1.is_set() == True:
            print('连接成功')
            break
        else:
            count += 1
            print('第%s次连接失败' % count)
    else:
        # 连接三次都都没连上,主动抛出异常
        raise TimeoutError('数据库连接超时')


def check_web(e2):
    time.sleep(random.randint(0, 3))
    e2.set()


if __name__ == '__main__':
    e = Event()
    t1 = Thread(target=connect_db, args=(e, ))
    t2 = Thread(target=check_web, args=(e, ))
    t1.start()
    t2.start()

25.6.条件

# 条件
#
# acquire release
# 一个条件被创建之初 默认有一个False状态
# False状态 会影响wait()一直处于等待状态
# notify(int数据类型) 制造一串钥匙
# 不大重要,只能停留在面试中
from threading import Condition, Thread


def func(con1, i1):
    con1.acquire()
    con1.wait()     # 等钥匙
    print('在第%s个循环里' % i1)
    con.release()


con = Condition()
for i in range(10):
    Thread(target=func, args=(con, i)).start()
while True:
    num = int(input('>>>'))
    con.acquire()
    con.notify(num)     # 造钥匙
    con.release()

25.7.定时器

# 用的不多,知道这个组件就好了
import time
from threading import Timer


def func():
    print('时间同步')


if __name__ == '__main__':
    while True:
        Timer(5, func).start()  # 非阻塞的
        time.sleep(5)

25.8.队列

# queue
import queue


q = queue.Queue()   # 队列先进先出
# q.put()
# q.get()
# q.put_nowait()
# q.get_nowait()
queue.LifoQueue()   # 栈 先进后出
q.put((50, 'a'))
q.put((30, 'r'))
q.put((1, 'z'))
q.put((1, 'd'))
print(q.get())

25.9.池

import time
from concurrent.futures import ThreadPoolExecutor


def func(n):
    time.sleep(2)
    print(n)
    return n*n


tpool= ThreadPoolExecutor(max_workers=5)   # 默认 不要超过cpu个数*5
t_list = []
for i in range(20):
    t = tpool.submit(func, i)   # 异步提交任务
    t_list.append(t)
tpool.shutdown()  # close+join
print('主线程')
for t in t_list:
    print('****', t.result())

 

26. IO多路复用和协程复习

26.1协程:

进程    启动多个进程 进程之间是由操作系统负责调用
线程    启动多个线程 真正被CPU执行的最小单位实际是线程
        开启一个线程 创建一个线程 寄存器 堆栈
        关闭一个线程
协程
        本质上是一个线程
        能够在多个任务之间切换来节省一些IO时间
实现并发的手段
def consumer():
    """创建一个生成器"""
    while True:
        x = yield
        print('处理了数据', x)


def producer():
    c = consumer()
    next(c)
    for i in range(10):
        print('生产了数据:', i)
        c.send(i)


producer()

真正的协程模块就是使用greenlet完成的切换

from greenlet import greenlet
def eat():
    print('eating start')
    g2.switch()     # 切换到play
    print('eating end')
    g2.switch()


def play():
    print('playing start ')
    g1.switch()
    print('playing end')


if __name__ == '__main__':
    # 用于切换线程
    g1 = greenlet(eat)
    g2 = greenlet(play)
    g1.switch()

放在开头,是为了识别time (IO)

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


def eat():
    print(threading.current_thread())   # 查看线程名字
    print('eating start')
    time.sleep(1)   # gevent 检测到停1s,则调到另外一个函数中
    print('eating end')


def play():
    print(threading.current_thread())
    print('playing start ')
    time.sleep(1)
    print('playing end')


if __name__ == '__main__':
    g1 = gevent.spawn(eat)  # 开启协程
    g2 = gevent.spawn(play)
    g1.join()
    g2.join()

进程和线程的任务切换由操作系统完成
协程任务之间的切换由程序(代码)完成 只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换实现并发效果

同步 和 异步 (网络操作常用协程)

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


def task():
    time.sleep(1)
    print(12345)


def sync():
    for i in range(10):
        task()


def async():
    g_list = []
    for i in range(10):
        g = gevent.spawn(task)
        g_list.append(g)
    gevent.joinall(g_list)  # for g in g_list :g.join()


if __name__ == '__main__':
    sync()
    async()
协程 : 能够在一个线程中实现并发效果的概念
       能够规避一些任务中的IO操作
       在任务的执行过程中,检测到IO就切换到其他任务

多线程  被弱化了
协程: 在一个线程上,提高cpu的利用率
协程相比于多线程的优势  切换的效率更快了


爬虫例子(正则基础)
请求过程中的IO等待

 

from gevent import monkey;monkey.patch_all()
import gevent
from urllib.request import urlopen


def get_url(url1):
    response = urlopen(url1)
    content = response.read().decode('utf-8')   # 有各式的
    return len(content)


url = {
    'http://www.baidu.com',
    'http://www.taobao.com',
    'http://www.hao123.com',
}
g_list = []
for i in url:
    g = gevent.spawn(get_url, i)
    g_list.append(g)
gevent.joinall(g_list)
for g in g_list:
    print(g.value)

    26.2用协程写 socket_demo
socket 

# 用协程写 socket
# 用协程是最快最方便的  最省时间占用最小,代码间的转换
from gevent import monkey; monkey.patch_all()
import socket
import gevent


def talk(conn1):
    conn1.send(b'hello')
    rec = conn.recv(1024).decode('utf-8')
    print(rec)
    conn.close()


if __name__ == '__main__':
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    while True:
        conn, add = sk.accept()
        gevent.spawn(talk, conn)
    sk.close()

client
import socket


sk = socket.socket()
sk.connect(('127.0.0.1', 8080))
rec = sk.recv(1024).decode('utf-8')
print(rec)
msg = input('>>>>').encode('utf-8')
sk.send(msg)
sk.close()

26.3IO模型 笔记

同步 : 提交一个任务之后要等待这个任务执行完毕
异步 : 只管提交任务,不等待这个任务执行完毕就可以做其他事情
阻塞 : input  urlopen()  在socket里面:recv() recvfrom() accept
非阻塞 : 除了阻塞的其他都是非阻塞

阻塞  线程  运行状态 --> 阻塞状态-->就绪
非阻塞

IO多路复用
  select机制   Windows和linux            都是操作系统轮询每一个被监听的项,看是否读操作
  poll机制      linux   它可以监听的对象比select机制可以监听的多
                          随着监听项的增多,导致效率降低
  epoll机制     linux

  26.4.非阻塞模型

socket

# 非阻塞IO模型
# 单线程中非阻塞!(没有用协程!)
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.setblocking(False)   # 默认True阻塞, False非阻塞
sk.listen()
conn_list = []
del_conn = []   # 存入失效连接的列表
while True:
    # 接收异常 BlockingIOError 完成非阻塞
    try:
        conn, add = sk.accept()     # 不阻塞,但没人连我会报错
        print('建立连接了', add)
        # msg = conn.recv(1024)       # 不阻塞,但没有消息会报错
        # print(msg)
        conn_list.append(conn)
    except BlockingIOError:
        # 循环列表连接 看看是否有人发消息
        for con in conn_list:
            try:
                msg = con.recv(1024)    # 不阻塞,但没有消息会报错
                if msg == b'':
                    del_conn.append(con)    # 把失效的连接存到del_conn中
                    continue
                print(msg)
                con.send(b'bye bye')
            except BlockingIOError:
                pass
        for con in del_conn:
            con.close()
            conn_list.remove(con)   # 在conn_list中删除失效连接
        del_conn.clear()    # 清空删除列表

client

# 非阻塞IO 多线程并发socket IO
import time
import socket
import threading


def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    sk.send(b'hello')
    time.sleep(1)
    msg = sk.recv(1024)
    print(msg)
    sk.close()


for i in range(20):
    threading .Thread(target=func).start()

26.5.IO多路复用 

socket

# IO多路复用 多并发!
import select
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.setblocking(False)
sk.listen()

read_list = [sk]  # 储存监听对象
while True:  # [sk, conn]  sk,发送链接    conn监听发送消息
    r_list, w_list, x_list = select.select(read_list, [], [])
    for i in r_list:
        if i is sk:
            conn, add = i.accept()  # 没有sk, 有conn则会报错
            read_list.append(conn)
        else:
            ret = i.recv(1024)
            if ret == b'':
                i.close()
                read_list.remove(i)
                continue
            print(ret)
            i.send(b'goodbye')

client

import socket
import threading
import time


def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    sk.send(b'hello')
    time.sleep(1)
    sk.recv(1024)
    sk.close()


for i in range(20):
    threading .Thread(target=func).start()

26.6selector_dome

# 服务端
from socket import *
import selectors
sel = selectors.DefaultSelector()


def accept(server_fileobj, mask):
    conn, addr = server_fileobj.accept()
    sel.register(conn, selectors.EVENT_READ, read)


def read(conn, mask):
    try:
        data = conn.recv(1024)
        if not data:
            print('closing', conn)
            sel.unregister(conn)
            conn.close()
            return
        conn.send(data.upper()+b'_SB')
    except Exception:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()


sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8088))
sk.listen(5)
sk.setblocking(False)  # 设置socket的接口为非阻塞
# 相当于网select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept
sel.register(sk, selectors.EVENT_READ, accept)
# 说白了就是,如果有人请求连接sk,就调用accept方法

while True:
    events = sel.select()  # 检测所有的sk,conn,是否有完成wait data的
    for sel_obj, mask in events:    # [sk]
        callback = sel_obj.data  # callback = accept
        callback(sel_obj.fileobjmask)  # accept(server_fileobj,1)
#客户端
# 基于selectors模块实现聊天
from socket import *
c=socket(AF_INET, SOCK_STREAM)
c.connect(('127.0.0.1',8088))

while True:
    msg=input('>>: ')
    if not msg:continue
    c.send(msg.encode('utf-8'))
    data = c.recv(1024)
    print(data.decode('utf-8'))

2018-9-23 16:18:40 终于整理完了

下次开始整理数据库内容 目前已经把博客python知识整理完了!好有成就感!

下次整理 7.26数据库 (1)

 

posted @ 2018-09-23 16:20  我想喝杨枝甘露~  阅读(1551)  评论(0编辑  收藏  举报