进程,线程池 ,同步异步

进程池,线程池

以时间换空间,控制进程,线程开启的数量

进程池与cpu一一对应是并行

线程池是并发:一个容器,这个容器限制住你开启线程(进程)的数量,比如4个,第一次肯定只能变更发的处理4个任务,只要有任务完成,线程马上就会接下一个人任务

以时间换空间,控制进程,线程开启的数量

进程池与cpu一一对应是(并行)或并行加并发

线程池是并发:一个容器,这个容器限制住你开启线程(进程)的数量,比如4个,第一次肯定只能变更发的处理4个任务,只要有任务完成,线程马上就会接下一个人任务

以时间换空间

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
def fun():
    print(1)
t=ThreadPoolExecutor()#实例化一个进程池对象
t.submit(fun)#用submit开启一个进程池
基于线程池的服务端
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import socket
import os
import time
import random


def communicate(conn, addr):

    while 1:
        #
        # try:

            from_client_data = conn.recv(1024)

            print(f'来自客户端{addr[1]}的消息: {from_client_data.decode("utf-8")}')

            to_client_data = input('>>>').strip()

            conn.send(to_client_data.encode('utf-8'))
if __name__ == '__main__':
    server = socket.socket()
    server.bind(('127.0.0.1', 8848))
    server.listen(5)
    print(1)
    while 1:
        con, addr = server.accept()
        t=ThreadPoolExecutor()
        t.submit(communicate,con,addr)

基于进程的服务端

import socket
from threading import Thread
import time
import random
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from multiprocessing import Process
def comniate(conn,addr):
    while 1:
        time.sleep(random.randint(1,3))
        for_client_data=conn.recv(1024)
        print(f'客户端{addr[1]}消息{for_client_data.decode("utf-8")}')
        time.sleep(random.randint(3,4))
        # to_client_data=input('>>>').strip()#不能使用input
        conn.send('121212'.encode('utf-8'))
    conn.close()


def func():
    server=socket.socket()
    server.bind(('127.0.0.1',8848))
    server.listen(5)
    print('开启')
    while 1:
        conn,addr=server.accept()

        t.submit(comniate,conn,addr)


if __name__ == '__main__':
    t = ProcessPoolExecutor(3)
    func()

阻塞非阻塞

阻塞,非阻塞程序两种运行状态

三种运行状态 运行,就绪,阻塞

遇到io操作就会发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且会立刻释放cpu资源(cpu切换到其他进程中),结束进入就绪态

非阻塞:没有遇到io操作,但是我通过某种手段,让cpu强行运行我的程序

(或者用某种手段让程序即使遇到io操作也不会停在原地,执行其他操作,力求尽可能多的占有cpu)

同步与异步

obj=pool.submit()#obj是动态对象

打印obj出现状态

obj.result()等待这个任务结束,返回结果,在执行下一个任务(加上这个result会变成同步)#需要等到拿到返回结果

提交任务的两种方式

同步调用:

提交完任务后(任务执行不完会被cpu会切走),(不管有计算还是遇到io)就在原地等待,直到任务运行完毕后,拿到任务的返回值(不一定有),才继续执行下一行代码同步调用和阻塞没有关系

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time
import random
import os

def task(i):
    print(f'{os.getpid()}开始任务')
    time.sleep(random.randint(1,3))
    print(f'{os.getpid()}任务结束')
    return i
if __name__ == '__main__':
    pool=ProcessPoolExecutor()
    for i in  range(20):
        obj=pool.submit(task,i)#开启子进程
        print(obj.result())#等待打印返回值#return 的返回值返回给了 result
        pool.shutdown(wait=True)
    print('===主')

多个任务,同步调用效率低

异步调用:

一次提交多个任务,然后直接执行下一行代码,

如何接收返回值

1.统一回收结果

假如异步发出10个任务,并发的执行,但是统一的接收所有的任务的返回值(效率低,不能实时的获取结果)

缺点:我不能马上收到任何一个已经完成的任务的返回值

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time
import random
import os
def task(i):
    print(f'{os.getpid()}开始任务')
    time.sleep(random.randint(1,3))
    print(f'{os.getpid()}任务结束')
    return i
if __name__ == '__main__':
    pool=ProcessPoolExecutor()
    li=[]
    for i in  range(20):
        obj=pool.submit(task,i)
        li.append(obj)
        # print(obj.result())  # 打印返回值
    pool.shutdown(wait=True)
    for i in li:
        print(i.result())

    print('===主')

2.完成一个任务接收一个返回结果(有问题)


import requests
from concurrent.futures import ProcessPoolExecutor
from concurrent.futures import ThreadPoolExecutor
def task(url):
    ret=requests.get(url)
    if ret.status_code==200:
        return parse(ret.text)
    #parse(ret.text)#一个线程除了取数据还需要清洗数据 时间长
def parse(obj):
    return(len(obj))


#def task(url):
    #ret=requests.get(url)
    #if ret.status_code==200:
        #return parse(ret.text)
#def parse(obj):
    #return(len(obj))


if __name__ == '__main__':
    pool=ThreadPoolExecutor(4)
    url_list=[
         'https://www.luffycity.com/',
        'https://www.baidu.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
    ]
    li=[]
    for url in url_list:
        obj=pool.submit(task,url)
        li.append(obj)
    pool.shutdown(wait=True)
    for i in li:
        print(i.result())#统一接收结果
#两个函数耦合性太高

shutdown:

只有线程 进程池里面有

1.让我的主进程池中所有的子进程都结束任务之后,类似join,一个任务是通过一个函数实现的,任务完成了的返回值是函数的返回值

2.执行时不允许别人添加执行任务,接受返回值就证明任务结束了

浏览器的工作原理

向服务端发送一个请求,服务端验证你的请求,如果正确,给你的浏览器返回一个文件,将文件里面的代码渲染成你看到的漂亮美丽的模样.

爬虫

1.利用代码模拟一个浏览器,浏览器的工作流程

2.数据清洗(处理源代码)

对源代码进程数据 清晰得到我想要的数据

import requests
response=requests.get('http://www.baidu.com')#拿到一个网址url
if response.status_code==200:   #200可以访问  
    print(response.text)#进行网页数据获取

异步+回调

运行完不等待返回值,返回值需统一回收,想要不统一,需要用到回调机制

进行

异步是io密集型,回调是计算密集型

异步统一回收,实时接收,回调接收处理结果

什么是回调函数?

按顺序接收每个任务的结果,进行下一步 处理.

import requests
from concurrent.futures import ProcessPoolExecutor
from concurrent.futures import ThreadPoolExecutor
def task(url):
    '''模拟的就是爬取多个源代码 一定有IO操作'''
    ret=requests.get(url)
    if ret.status_code==200:
        return ret.text
def parse(obj):
    '''模拟对数据进行分析 一般没有IO'''
    print(len(obj.result()))


if __name__ == '__main__':
    pool=ThreadPoolExecutor(4)
    url_list=[
         'https://www.luffycity.com/',
        'https://www.baidu.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
        'https://www.luffycity.com/',
    ]
    for url in url_list:
        obj=pool.submit(task,url)
        obj.add_done_callback(parse)#回调函数
   #线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码, 并发执行,
   # 当一个任务完成之后,将parse这个分析代码的任务交由剩余的空闲的线程去执行,你这个线程继续去处理其他任务.
 #进程池+回调是全部交由主进程进行运行
#线程池+回调是全部交由空闲线程运行

线程queue

十四 线程队列

  线程之间的通信我们列表行不行呢,当然行,那么队列和列表有什么区别呢

  queue队列 :使用import queue,用法与进程Queue一样

  • class queue.Queue(maxsize=0) #先进先出

img

复制代码

import queue #不需要通过threading模块里面导入,直接import queue就可以了,这是python自带的
#用法基本和我们进程multiprocess中的queue是一样的
q=queue.Queue()
q.put('first')
q.put('second')
q.put('third')
# q.put_nowait() #没有数据就报错,可以通过try来搞
print(q.get())
print(q.get())
print(q.get())
# q.get_nowait() #没有数据就报错,可以通过try来搞
'''
结果(先进先出):
first
second
third
'''

先进先出示例代码

  class queue.LifoQueue(maxsize=0) #last in fisrt out

import queue
q=queue.LifoQueue() #队列,类似于栈,先进后出的顺序
q.put('first')
q.put('second')
q.put('third')
# q.put_nowait()

print(q.get())
print(q.get())
print(q.get())
# q.get_nowait()
'''
结果(后进先出):
third
second
first
'''

先进后出示例代码

 class queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列

第三种 优先级队列 数字越低 优先级越高

import queue

q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((-10,'a'))
q.put((-5,'a'))  #负数也可以
# q.put((20,'ws'))  #如果两个值的优先级一样,那么按照后面的值的acsii码顺序来排序,如果字符串第一个数元素相同,比较第二个元素的acsii码顺序
# q.put((20,'wd'))
# q.put((20,{'a':11})) #TypeError: unorderable types: dict() < dict() 不能是字典
# q.put((20,('w',1)))  #优先级相同的两个数据,他们后面的值必须是相同的数据类型才能比较,可以是元祖,也是通过元素的ascii码顺序来排序

q.put((20,'b'))
q.put((20,'a'))
q.put((0,'b'))
q.put((30,'c'))

print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
'''

优先级队列示例代码

这三种队列都是线程安全的,不会出现多个线程抢占同一个资源或数据的情况。

多线程抢占资源,串行

第一种 先进先出

q=queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get(block=false))
q.get(timeout=2)

第二种后进先出 堆栈 lifo

1728961-20190829171332266-1556444572.png

事件event

进程使用标志位FALG=FALSE

开启两个线程,一个线程运行到中间的某个阶段,触发另 个线程执行.两个线程增加了耦合性.

event.wait()#等待
event.set()#ture
is_set==isSet#判断是否为ture                             方法

多进程

from threading import Thread,current_thread,Event
from multiprocessing import Process,queues,Event
from concurrent.futures import ThreadPoolExecutor
import time
event=Event()

def check(q):

        print('%s 正常检测服务是否正常。。。'% current_thread().name)
        time.sleep(1)
        q.set()

def connect(q):
    num = 1
    while not q.is_set():
        if num==4:
            print('%s 连接失败' % current_thread().name)
            break
        else:
            print("%s 开始尝试连接%s次"%(current_thread().name,num))
            q.wait(1)
            num+=1
    else:
        print('%s 连接成功' % current_thread().name)
if __name__ == '__main__':
    # q=queues.Queue()
    event=Event()
    t1=Process(target=connect,args=(event,))
    t2=Process(target=connect,args=(event,))
    t3=Process(target=connect,args=(event,))
    T4=Process(target=check,args=(event,))
    T4.start()
    t1.start()
    t2.start()
    t3.start()

多线程

from threading import Thread,current_thread,Event
from multiprocessing import Process
from concurrent.futures import ThreadPoolExecutor
import time
event=Event()

def check():

        print('%s 正常检测服务是否正常。。。'% current_thread().name)
        time.sleep(3)
        event.set()


def connect():
    num = 1
    while not event.is_set():
        if num==4:
            print('%s 连接失败' % current_thread().name)
            break
        else:
            print("%s 开始尝试连接%s次"%(current_thread().name,num))
            event.wait(2)
            num+=1
    else:
        print('%s 连接成功' % current_thread().name)
if __name__ == '__main__':
    t1=Thread(target=connect)
    t2=Thread(target=connect)
    t3=Thread(target=connect)
    T4=Thread(target=check)
    T4.start()
    t1.start()
    t2.start()
    t3.start()

开启两个线程,一个线程允许到中间某个阶段,触发另一个线程执行

两个线程增加耦合性

协程(单线程下)

协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。

python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)

什么是协程?

单个线程并发的处理多个任务. 程序控制协程的切换+保持状态.

单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

并发

如何实现并发 多线程 多进程 并发去执行,携程也是并发

并行

多个cpu执行多个任务,4个cpu执行4个任务 速度快开销大需要开启4个进程

串行

一个线程执行一个任务,执行完毕之后,执行下一个任务

并发的核心

切换并且保持状态

多线程并发

3个线程处理10个任务

多线程遇到阻塞执行下一个线程 操作系统强行把cpu切到下一个线程(例如三个线程,一个线程阻塞,下一个线程调用cpu,)

多线程切换是cpu在不同线程下的切换,cpu只能运行线程(不能主动切换),线程是最小的执行单位

协程(单线程下并发的操作流程)

单个线程并发的处理多个任务,程序控制协程的切换+保持状态

协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度

一个线程运行多个任务(单线程的并发)

协程和cpu的关系

cpu一直运行线程,当遇到阻塞,协程调用cpu执行下一个任务遇到,阻塞切换到下一个任务(遇到阻塞切换是线程在三个任务之间来回切换,(多线程是指cpu在不同线程之间切换),cpu一直运行线程,线程没有停

利用多线程io的阻塞时间 进行下一个线程的操作

10个任务,让你给我并发10个任务

协程最好

前提一个cpu

    1.开启多进程(有主线程)并发执行。--操作系统--时刻监视cpu(cpu停了)遇到阻塞或者运行时间过长就把cpu切走)切换+保持状态

​   2.开启多线程并发执行 --操作系统--时刻监视cpu)遇到阻塞(同时阻塞)或者运行时间过长(操作系统)切换+保持状态

​   3.(一个线程并发执行任务)开启协程并发执行,自己的程序(代码)绑定cpu 让cpu一直工作,把控着cpu在3个任务之间来回切换+保持状态
对3详细解释:协程他切换速度非常快,蒙蔽操作系统的眼睛,让操作系统认为cpu一直在运行你这一个线程(协程)

协程的优点:

1.运行速度快,单线程内就可以实现并发效果

2.协程会长期霸占cpu只执行我的程序效率高

3.携程开销小,

4.单线程并发最大限度利用cpu

缺点

协程单线程本质上是单线程下的无法利用多核

(一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程)

协程指的是单个线程,一旦出现阻塞,将会阻塞整个线程 cpu被操作系统切走,整个线程结束

总结协程特点:

1.必须在只有一个单线程里实现并发

2.修改共享数据不需加锁(因为是一个线程,多线程需要加锁,多个任务修改数据共享 )

3.用户程序里自己保存多个控制流的上下文栈(保持状态)

4一个协程遇到io操作自动切换到其他协程

并发的本质:切换+保持状态

协程处理io密集型,计算密集型,还剩串行好

携程一直阻塞会来回切 整个线程就会结束 ,操作系统会切走cpu,结束了

greenlet

模块里 有一个机制强行不让cpu切换

任务之间来回切 切换加保存状态 遇到io不会切换需要等

g2.switch()#切换保存状态

from greenlet import greenlet

def eat(name):
    print('%s eat 1' %name)  #2
    g2.switch('taibai')   #3
    print('%s eat 2' %name) #6
    g2.switch() #7
def play(name):
    print('%s play 1' %name) #4
    g1.switch()      #5
    print('%s play 2' %name) #8

g1=greenlet(eat)
g2=greenlet(play)

g1.switch('taibai')

Gevent

模块里 有一个机制强行不让cpu切换

spawn(cls, *args, **kwargs)
#传参依次是(函数,位置参数,默认参数)
g1=gevent.spawn(eat,'eg'(位置传参))#前面一个是函数,第二个参数是位置传参
g2=gevent.spawn(play,name='on'(默认传参))

import gevent#引用一个第三方模块
# from multiprocessing import Process
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)#这个阻塞是gevent的阻塞
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    gevent.sleep(1)
    print('%s play 2' %name)


g1=gevent.spawn(eat,'eg')#前面一个是函数,第二个参数是位置传参
g2=gevent.spawn(play,name='on')
'''
为什么加join就运行了
一个线程相当于虚拟了两个线程
因为主线程结束了 join限制主线程不结束
'''
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('主')
最终版本把下面所有任务的阻塞都打上标记
import gevent
from gevent import monkey
monkey.patch_all()  # 一定要写到最上面打补丁: 将下面的所有的任务的阻塞都打上标记
def eat(name):
    print('%s eat 1' %name)
    time.sleep(2)#阻塞切换
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    time.sleep(1)#阻塞一秒
    print('%s play 2' %name)


g1 = gevent.spawn(eat,'egon')
g2 = gevent.spawn(play,name='egon')

# g1.join()
# g2.join()
gevent.joinall([g1,g2])
协程下面的两个任务阻塞,切换一两次就被操作系统讲cpu拿走了,当一个任务一个任务最先阻塞完毕之后,协程就会将cpu,抢回来执行

转载于:https://www.cnblogs.com/strawberry-1/p/11430788.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值