网络并发

 网络并发

一 操作系统的历史与初步认识

1.人机矛盾:

  CPU利用率低

  磁带存储 + 批处理

  降低数据的读取时间

  提高cpu的利用率

2.多道操作系统—— 在一个任务遇到IO的时候主动让出CPU

  数据隔离

  时空复用

  能够在一个任务遇到io操作的时候主动把cpu让出来,给其他的任务使用

3.分时操作系统 —— 给时间分片,让多个任务轮流使用CPU

4.分布式操作系统

5.网络操作系统

 

二.进程

进程初识:

进程也就是说是运行中的程序  :是计算机最小额资源分配单位:进程的调度室友操作系统完成的;

三状态:

 就绪 -system call-><-时间片到了- 运行 -io-> 阻塞-IO结束->就绪

    阻塞 影响了程序运行的效率

同步与异步:

  同步:调用一个方法要等待这个方法结束

  异步:调用一个方法 不等待这个方法结束 也不关心这个方法做了什么

阻塞与非阻塞:

  阻塞:cpu不工作

  非阻塞:cpu工作

并发与并行:

  并发:并发是指多个程序 公用一个cpu轮流使用。

     并行:多个程序 多个cpu 一个cpu上运行一个程序,

   在一个时间点上看,多个程序同时在多个cpu上运行。

    

进程的开启与结束:

1.开启一个进程:

  

函数名(参数1,参数2)
    from multiprocessing import Process
    p = Process(target=函数名,args=(参数1,参数2))
    p.start()

2.父进程和子进程

3.父进程会等待着所有的子进程结束之后才结束:为了回收资源

4.:进程开启的过程中windows和 linux/ios之间的区别

      开启进程的过程需要放在if __name__ == '__main__'下windows中 相当于在子进程中把主进程文件又从头到尾执行了一遍,

    除了放在if __name__ == '__main__'下的代码,linux中 不执行代码,直接执行调用的func函数

5.join的方法:

    把一个进程的结束事件封装成一个join方法

      执行join方法的效果就是 阻塞直到这个子进程执行结束就结束阻塞

    在多个子进程中使用join
    p_l= []
    for i in range(10):
        p = Process(target=函数名,args=(参数1,参数2))
        p.start()
        p_l.append(p)
    for p in p_l:p.join()
    所有的子进程都结束之后要执行的代码写在这里

6.mulitiprocessing进程

  p=Process(target=函数名。arge = (参数1,)

7.实例

import demo
from multiprocessing import Process

def target(i):
    print(i)

if __name__ == '__main__':
    p_l = []
    for i in range(5):
        p = Process(target=target,args=(i,))
        p.start()
        p_l.append(p)
    # p.join()  # 阻塞 主进程 直到子进程执行完毕
    for p in p_l:p.join()
    print('子进程已经都执行完毕了')
        mp2 = MyProcecss2()
    mp2.start()
    print('main :',os.getpid())
    time.sleep(1)

8..守护进程

1.例子1

import time
from multiprocessing import Process

def son1(a,b):
    while True:
        print('is alive')
        time.sleep(0.5)

def son2():
    for i in range(5):
        print('in son2')
        time.sleep(1)

if __name__ == '__main__':
    p = Process(target=son1,args=(1,2))
    p.daemon = True
    p.start()      # 把p子进程设置成了一个守护进程
    p2 = Process(target=son2)
    p2.start()
    time.sleep(2)

2.面向对象

# 有一个参数可以把一个子进程设置为一个守护进程
import time
from multiprocessing import Process

def son1():
    while True:
        print('is alive')
        time.sleep(0.5)

if __name__ == '__main__':
    p = Process(target=son1)
    p.start()      # 异步 非阻塞
    print(p.is_alive())
    time.sleep(1)
    p.terminate()   # 异步的 非阻塞
    print(p.is_alive())   # 进程还活着 因为操作系统还没来得及关闭进程
    time.sleep(0.01)
    print(p.is_alive())   # 操作系统已经响应了我们要关闭进程的需求,再去检测的时候,得到的结果是进程已经结束了

9.进程之间的通信

进程之间的通信 - IPC(inter process communication)

# from multiprocessing import Queue,Process
# # 先进先出
 def func(exp,q):
     ret = eval(exp)
      q.put({ret,2,3})
      q.put(ret*2)
       q.put(ret*4)

 if __name__ == '__main__':
     q = Queue()
     Process(target=func,args=('1+2+3',q)).start()
     print(q.get())
     print(q.get())
     print(q.get())

 

三.线程

1.线程的优缺点:

  线程是进程的一部分,每个进程中至少有一个线程

  能被CPU调度的最小单位

  一个进程中的多个线程是可以共享这个进程的数据的

  线程的创建,销毁,切换,开销远远小于进程---开销小

       不能利用多核

2.threading模块:

   线程中的几个方法:

  属于线程对象t.start(),t.join()

   守护线程t.daemon = True 等待所有的非守护子线程都结束之后才结束

            非守护线程不结束,主线程也不结束

      主线程结束了,主进程也结束

      结束顺序 :非守护线程结束 -->主线程结束-->主进程结束

      -->主进程结束 --> 守护线程也结束

3.线程之间的通信——队列

from queue import Queue  # 先进先出队列
# q = Queue(5)
# q.put(0)
# q.put(1)
# q.put(2)
# q.put(3)
# q.put(4)
# print('444444')
#
#
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get())

后进先出

from queue import LifoQueue  # 后进先出队列
# last in first out 栈
# lfq = LifoQueue(4)
# lfq.put(1)
# lfq.put(3)
# lfq.put(2)
# print(lfq.get())
# print(lfq.get())
# print(lfq.get())

优先级

# from queue import PriorityQueue
# pq = PriorityQueue()
# pq.put((10,'alex'))
# pq.put((6,'wusir'))
# pq.put((20,'yuan'))
# print(pq.get())
# print(pq.get())
# print(pq.get())

四.协程

# 线程是由 操作系统 调度,由操作系统负责切换的
# 协程:
    # 用户级别的,由我们自己写的python代码来控制切换的
    # 是操作系统不可见的
# 在Cpython解释器下 - 协程和线程都不能利用多核,都是在一个CPU上轮流执行
    # 由于多线程本身就不能利用多核
    # 所以即便是开启了多个线程也只能轮流在一个CPU上执行
    # 协程如果把所有任务的IO操作都规避掉,只剩下需要使用CPU的操作
    # 就意味着协程就可以做到题高CPU利用率的效果
# 多线程和协程
    # 线程 切换需要操作系统,开销大,操作系统不可控,给操作系统的压力大
        # 操作系统对IO操作的感知更加灵敏
    # 协程 切换需要python代码,开销小,用户操作可控,完全不会增加操作系统的压力
        # 用户级别能够对IO操作的感知比较低

协程切换问题

# 协程 :能够在一个线程下的多个任务之间来回切换,那么每一个任务都是一个协程
# 两种切换方式
    # 原生python完成   yield  asyncio
    # C语言完成的python模块  greenlet  gevent

# greenlet
# import time
# from  greenlet import greenlet
#
# def eat():
#     print('wusir is eating')
#     time.sleep(0.5)
#     g2.switch()
#     print('wusir finished eat')
#
# def sleep():
#     print('小马哥 is sleeping')
#     time.sleep(0.5)
#     print('小马哥 finished sleep')
#     g1.switch()
#
# g1 = greenlet(eat)
# g2 = greenlet(sleep)
# g1.switch()

gevent模块

# import time
# print('-->',time.sleep)
# import gevent
# from gevent import monkey
# monkey.patch_all()
# def eat():
#     print('wusir is eating')
#     print('in eat: ',time.sleep)
#     time.sleep(1)
#     print('wusir finished eat')
#
# def sleep():
#     print('小马哥 is sleeping')
#     time.sleep(1)
#     print('小马哥 finished sleep')
#
# g1 = gevent.spawn(eat)  # 创造一个协程任务
# g2 = gevent.spawn(sleep)  # 创造一个协程任务
# g1.join()   # 阻塞 直到g1任务完成为止
# g2.join()   # 阻塞 直到g1任务完成为止

 

五.锁

1.实例1

import time
import json
from multiprocessing import Process,Lock

def search_ticket(user):
    with open('ticket_count') as f:
        dic = json.load(f)
        print('%s查询结果  : %s张余票'%(user,dic['count']))

def buy_ticket(user,lock):
    # with lock:
    # lock.acquire()   # 给这段代码加上一把锁
        time.sleep(0.02)
        with open('ticket_count') as f:
            dic = json.load(f)
        if dic['count'] > 0:
            print('%s买到票了'%(user))
            dic['count'] -= 1
        else:
            print('%s没买到票' % (user))
        time.sleep(0.02)
        with open('ticket_count','w') as f:
            json.dump(dic,f)
    # lock.release()   # 给这段代码解锁

def task(user, lock):
    search_ticket(user)
    with lock:
        buy_ticket(user, lock)

if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        p = Process(target=task,args=('user%s'%i,lock))
        p.start()

2.互斥锁

# 互斥锁是锁中的一种:在同一个线程中,不能连续acquire多次
# from threading import Lock
# lock = Lock()
# lock.acquire()
# print('*'*20)
# lock.release()
# lock.acquire()
# print('-'*20)
# lock.release()

 

3.递归锁

含义:在同一个进程中多次acquire多次不会被锁住

好 :在同一个进程中多次acquire也不会发生阻塞

不好 :占用了更多资源

  递归锁解决死锁的问题

import time
from threading import RLock,Thread
noodle_lock = fork_lock = RLock()
print(noodle_lock,fork_lock)
def eat1(name,noodle_lock,fork_lock):
    noodle_lock.acquire()
    print('%s抢到面了'%name)
    fork_lock.acquire()
    print('%s抢到叉子了' % name)
    print('%s吃了一口面'%name)
    time.sleep(0.1)
    fork_lock.release()
    print('%s放下叉子了' % name)
    noodle_lock.release()
    print('%s放下面了' % name)

def eat2(name,noodle_lock,fork_lock):
    fork_lock.acquire()
    print('%s抢到叉子了' % name)
    noodle_lock.acquire()
    print('%s抢到面了'%name)
    print('%s吃了一口面'%name)
    time.sleep(0.1)
    noodle_lock.release()
    print('%s放下面了' % name)
    fork_lock.release()
    print('%s放下叉子了' % name)

lst = ['alex','wusir','taibai','yuan']
Thread(target=eat1,args=(lst[0],noodle_lock,fork_lock)).start()
Thread(target=eat2,args=(lst[1],noodle_lock,fork_lock)).start()
Thread(target=eat1,args=(lst[2],noodle_lock,fork_lock)).start()
Thread(target=eat2,args=(lst[3],noodle_lock,fork_lock)).start()

 

六.池

#
    # 进程池
    # 线程池
#
# 预先的开启固定个数的进程数,当任务来临的时候,直接提交给已经开好的进程
# 让这个进程去执行就可以了
# 节省了进程,线程的开启 关闭 切换都需要时间
# 并且减轻了操作系统调度的负担

concurrent.futures 进程池

concurrent.futures
import os
import time
import random
from concurrent.futures import ProcessPoolExecutor
# submit + shutdown
# def func():
#     print('start',os.getpid())
#     time.sleep(random.randint(1,3))
#     print('end', os.getpid())
# if __name__ == '__main__':
#     p = ProcessPoolExecutor(5)
#     for i in range(10):
#         p.submit(func)
#     p.shutdown()   # 关闭池之后就不能继续提交任务,并且会阻塞,直到已经提交的任务完成
#     print('main',os.getpid())

带参数和返回值的进程池

# def func(i,name):
#     print('start',os.getpid())
#     time.sleep(random.randint(1,3))
#     print('end', os.getpid())
#     return '%s * %s'%(i,os.getpid())
# if __name__ == '__main__':
#     p = ProcessPoolExecutor(5)
#     ret_l = []
#     for i in range(10):
#         ret = p.submit(func,i,'alex')
#         ret_l.append(ret)
#     for ret in ret_l:
#         print('ret-->',ret.result())  # ret.result() 同步阻塞
#     print('main',os.getpid())
缺点:开销大,一个池中的任务个数限制了我们程序的并发个数

线程池

# from concurrent.futures import ThreadPoolExecutor
# def func(i):
#     print('start', os.getpid())
#     time.sleep(random.randint(1,3))
#     print('end', os.getpid())
#     return '%s * %s'%(i,os.getpid())
# tp = ThreadPoolExecutor(20)
# ret_l = []
# for i in range(10):
#     ret = tp.submit(func,i)
#     ret_l.append(ret)
# tp.shutdown()
# print('main')
# for ret in ret_l:
#     print('------>',ret.result())

回调函数

import requests
from concurrent.futures import ThreadPoolExecutor
def get_page(url):
    res = requests.get(url)
    return {'url':url,'content':res.text}

def parserpage(ret):
    dic = ret.result()
    print(dic['url'])
tp = ThreadPoolExecutor(5)
url_lst = [
    'http://www.baidu.com',   # 3
    'http://www.cnblogs.com', # 1
    'http://www.douban.com',  # 1
    'http://www.tencent.com',
]
ret_l = []
for url in url_lst:
    ret = tp.submit(get_page,url)
    ret_l.append(ret)
    ret.add_done_callback(parserpage)

 

posted @ 2019-05-26 22:00  三枫博客  阅读(648)  评论(0编辑  收藏  举报