10-线程,进程,协程,IO多路复用

Posted 汉克书

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10-线程,进程,协程,IO多路复用相关的知识,希望对你有一定的参考价值。

- 线程进程介绍
1. 工作最小单元是线程
2. 应用程序 -> 至少有一个进程 -> 至少有一个线程
3. 应用场景:
IO密集型:线程
计算密集型:进程
4. GIL,全局解释器锁。
- 保证同一个进程中只有一个线程同时被调度
- 线程
1. 基本使用
def task(arg):
    time.sleep(arg)
    print(arg)
for i in range(5):
    t = threading.Thread(target=task,args=[i,])
    # t.setDaemon(True) # 主线程终止,不等待子线程
    # t.setDaemon(False)
    t.start()
    # t.join()  # 一直等
    # t.join(1) # 等待最大时间
  2. 锁
# 1. 只能有一个人使用锁
    # lock = threading.Lock() # 只能开一把
    # lock = threading.RLock()# 可以开多把
# 2. 多个人同时使用锁
    # lock = threading.BoundedSemaphore(3)
# 3. 所有的解脱锁的限制
    # lock = threading.Event()
# 4. 肆意妄为
    # lock = threading.Condition()
3. 线程池
模式一:直接处理
        3. 线程池
            模式一:直接处理
def task(url):
    """
    任务执行两个操作:下载;保存本地
    """
    # response中封装了Http请求响应的所有数据
    # - response.url            请求的URL
    # - response.status_code    响应状态码
    # - response.text           响应内容(字符串格式)
    # - response.content        响应内容(字节格式)
    # 下载
    response = requests.get(url)

    # 下载内容保存至本地
    f = open(‘a.log‘,‘wb‘)
    f.write(response.content)
    f.close()
pool = ThreadPoolExecutor(2)
url_list = [
    ‘http://www.oldboyedu.com‘,
    ‘http://www.autohome.com.cn‘,
    ‘http://www.baidu.com‘,
]
for url in url_list:
    print(‘开始请求‘,url)
    # 去连接池中获取链接
    pool.submit(task,url)
模式二:分步处理
            模式二:分步处理
def save(future):
    """
    只做保存  # future中包含response
    """
    response = future.result()

    # 下载内容保存至本地
    f = open(‘a.log‘,‘wb‘)
    f.write(response.content)
    f.close()
def task(url):
    """
    只做下载 requests
    """
    # response中封装了Http请求响应的所有数据
    # - response.url            请求的URL
    # - response.status_code    响应状态码
    # - response.text           响应内容(字符串格式)
    # - response.content        响应内容(字节格式)
    # 下载
    response = requests.get(url)
    return response
pool = ThreadPoolExecutor(2)
url_list = [
    ‘http://www.oldboyedu.com‘,
    ‘http://www.autohome.com.cn‘,
    ‘http://www.baidu.com‘,
]
for url in url_list:
    print(‘开始请求‘,url)
    # 去连接池中获取链接
    # future中包含response
    future = pool.submit(task,url)
    # 下载成功后,自动调用save方法
    future.add_done_callback(save)
- 进程
1. 基本使用
from multiprocessing import Process
import time
def task(arg):
    time.sleep(arg)
    print(arg)
if __name__ == ‘__main__‘:
    for i in range(10):
        p = Process(target=task,args=(i,))
        p.daemon = True
        # p.daemon = False
        p.start()
        p.join(1)

    print(‘主进程最后...‘)
进程锁和线程锁是一样的(使用方式一样)
2. 进程之间的数据共享
特殊的东西
- Array(‘类型’,长度)
- Manager().list() / Manager().dict()
- 报错是因为,主进程走完了,子进程就就会报错
- 写一个input或者什么的,让主进程等,就能防止报错
- 或者jion成为串行。
- 使用进程池,就可以避免
- 第三方工具
from multiprocessing import Process
from threading import Thread

"""
# 验证进程之间数据不共享
def task(num,li):
    li.append(num)
    print(li)   # 数据不是共享的,打印出来就是一个个数字 [1] [2] 。。。

if __name__ == ‘__main__‘:
    v = []
    for i in range(10):
        # p = Process(target=task,args=(i,v,))
        p = Thread(target=task,args=(i,v,))
        p.start()
"""

"""
# 方式一:进程数据共享
from multiprocessing import Process,Array
from threading import Thread

def task(num,li):
    li[num] = 1
    print(list(li))

if __name__ == ‘__main__‘:
    v = Array(‘i‘,10) # v[0]
    for i in range(10):
        p = Process(target=task,args=(i,v,))
        p.start()
"""
from multiprocessing import Process,Manager
from threading import Thread

def task(num,li):
    li.append(num)
    print(li)

if __name__ == ‘__main__‘:
    v = Manager().list()
    # v = Manager().dict()
    for i in range(10):
        p = Process(target=task,args=(i,v,))
        p.start()
        # p.join()
    input(‘>>>‘)
3. 进程池
 - from concurrent.futures import ProcessPoolExecutor
   def call(arg):
         data = arg.result()
       print(data)
   def task(arg):
       print(arg)
       return arg + 100
   if __name__ == ‘__main__‘:
       pool = ProcessPoolExecutor(5)
       for i in range(10):
           obj = pool.submit(task,i)
           obj.add_done_callback(call)

================== 结论 ==================
IO密集:线程
计算密集:进程
- 协程 
pip3 install greenlet
协程永远是一个线程在执行,对线程的一个分片处理。
from greenlet import greenlet

def test1():
    print(12)
    gr2.switch()
    print(34)
    gr2.switch()

def test2():
    print(56)
    gr1.switch()
    print(78)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

二次加工:
自定义:
select实现
现成 :
pip3 install gevent
from gevent import monkey; monkey.patch_all()
import gevent
import requests

def f(url):
    response = requests.get(url)
    print(response.url,response.status_code)

gevent.joinall([
        gevent.spawn(f, ‘http://www.oldboyedu.com/‘),
        gevent.spawn(f, ‘http://www.baidu.com/‘),
        gevent.spawn(f, ‘http://github.com/‘),
])
- IO多路复用
监听多个socket对象是否有变化(可读,可写,发送错误)
- 示例一:
import socket
import select

# IO多路复用:8002,8001
#
############################### 基于select实现服务端的“伪”并发 ###############################
sk1 = socket.socket()
sk1.bind((‘127.0.0.1‘,8001,))
sk1.listen(5)

sk2 = socket.socket()
sk2.bind((‘127.0.0.1‘,8002,))
sk2.listen(5)
inputs = [sk1,sk2,]
w_inputs = []
while True:
    # IO多路复用,同时监听多个socket对象
    #    - select,内部进行循环操作(1024)  主动查看
    #    - poll, 内部进行循环操作         主动查看
    #    - epoll,                        被动告知
    r,w,e = select.select(inputs,w_inputs,inputs,0.05)  # r: readble w: writable e:异常
    # r = [sk2,]
    # r = [sk1,]
    # r = [sk1,sk2]
    # r = []
    # r = [conn,]
    # r = [sk1,Wconn]
    #######?
    for obj in r:
        if obj in [sk1,sk2]:
            # 新连接捡来了...
            print(‘新连接来了:‘,obj)
            conn,addr = obj.accept()
            inputs.append(conn)
        else:
            # 有连接用户发送消息来了..
            print(‘有用户发送数据了:‘,obj)
            try:
                data = obj.recv(1024)
            except Exception as ex:
                data = ""
            if data:
                w_inputs.append(obj)
                # obj.sendall(data)
            else:
                obj.close()
                inputs.remove(obj)
                w_inputs.remove(obj)

    for obj in w:
        obj.sendall(b‘ok‘)
        w_inputs.remove(obj)

#  Socket对象
sk1 = socket.socket()
sk1.bind((‘127.0.0.1‘,8001,))
sk1.listen(5)
while True:
    # conn Socket对象,
    conn,addr = sk.accept()
    conn.recv()
    conn.sendall()


- socketserverIO
- IO多路复用
- 线程
import socket
import select
import threading

# IO多路复用:8002,8001
#
############################### 基于select实现服务端的“伪”并发 ###############################
"""
def process_request(conn):
    while True:
        v = conn.recv(1024)
        conn.sendall(b‘1111‘)

sk1 = socket.socket()
sk1.bind((‘127.0.0.1‘,8001,))
sk1.listen(5)
inputs=[sk1,]
while True:
    # IO多路复用,同时监听多个socket对象
    #    - select,内部进行循环操作(1024)  主动查看
    #    - poll, 内部进行循环操作         主动查看
    #    - epoll,                        被动告知
    r,w,e = select.select(inputs,[],inputs,0.05)

    for obj in r:
        if obj in sk1:
            # conn客户端的socket
            conn,addr = obj.accept()
            t = threading.Thread(target=process_request,args=(conn,))
            t.start()
"""
# import socketserver
#
# class MyHandler(socketserver.BaseRequestHandler):
#     def handle(self):
#         pass
#
#
# server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8001),MyHandler)
# server.serve_forever()

- 自定义异步非阻塞的框架
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于10-线程,进程,协程,IO多路复用的主要内容,如果未能解决你的问题,请参考以下文章

python进程线程协程IO多路复用

进程,线程,协程,io多路复用 总结

IO多路复用, 基于IO多路复用+socket实现并发请求(一个线程100个请求), 协程

python全栈开发day36-IO多路复用

协程与IO多路复用

协程/IO多路复用