一. 目录

  1.进程的概念和两种创建方式

  2.多进程爬虫

  3.守护进程

  4.进程队列

  5.进程队列简单应用(实现数据共享)

  6.线程的两种创建方式

  7.线程和进程的效率对比

  8.线程共享统一进程的数据

  9.死锁现象

  10.线程队列的三种应用

  11.多线程执行计算密集型任务

  12. 线程池和进程池

  13. 回调函数

  14.守护线程

  15. 协程

  16.GlL 全局解释器锁

二. 内容

一.进程的概念和两种创建方式

  专业词描述:

  1. 操作系统的两大作用 1.把硬件丑陋复杂的接口隐藏起来,为应用程序提供良好的接口
    2.管理,调度进程,并且把进程之间对硬件的竞争变的有序化
  2.  
  3. 多道技术:
    1.产生背景:为了实现单cpu下的并发效果
    2.分为两个部
    1.空间上的复用(必须实现硬件层面的隔离)
    2.时间上的复用(复用的是cpu的时间片)
    什么时候切换?
    1.正在执行的任务遇到阻塞
    2.正在执行的任务运行时间过程(系统控制的)
  4.  
  5. 进程:正在运行的一个过程,一个任务,由操作系统负责调度,由cpu负责 执行
    程序:程序员写的代码
    并发:伪并行,单核+多道
    并行:只有多核才能实现真正的并行
    同步:一个进程在执行某个任务时,另外一个进程必须等待其执行完毕才能往下走
    异步:一个进程在执行某个任务时,另外一个进程无须等待其执行完毕,继续往下走
  6.  
  7. 进程的创建:
    1.系统初始化
    2.与用户交互
    3.执行一个进程的过程中的调用
    4.批处理任务
  8.  
  9. 系统的调用
    1. linuxfork
    2.window:CreateProcess
  10.  
  11. linux下的进程与windows的区别:
    1 linux的进程有父子关系,是一种树形结构,是父子关系,windows没这种关系
    2Linux创建新的进程需要copy父进程的地址空间,winx下最开始创建进程,两个进程之间不一样。

 进程的概述:进程是正在执行的程序的实例,是操作系统动态执行的基本单元。进程是一个实体,每一个进程都都有自己的地址空间,一般包括文本区域(python的文件) 、数据区域(python文件中的一些变量数据)和堆栈。文本区域存储处理执行的代码,数据区域存储变量的进程执行期间使用的动态分配内存。堆栈区域存储活动过程调用的指定和本地变量。

进程的终止:

1.正常退出

2.出错退出

3.严重错误

4.被其他程杀死

在windows中只有句柄的概念

进程的三种状态:就绪 运行 阻塞

进程并发的实现:进程表里面会记录程序上次执行的状态,一遍下次执行的时候接着执行。

 开启多进程方法一:

  1. import os
    import time
    import os
    import random
    from multiprocessing import Process
    print(os.cpu_count()) #查看有几个cpu
    def func():
    print("func funcation")
    time.sleep(random.randint(1,3))
  2.  
  3. if __name__ == '__main__':
    f = Process(target=func,name="p2") #指定进程名字
    f.start() #告诉系统我要创建一个子进程
    print("f name is %s" %f.name) #m默认从process -1开始f name is Process-1,可以自己指定
    print("主进程")
    # 进程要等到子进程执行完才能结束,否则子进程就变成僵尸进程了
  4.  
  5. 方法二:通过定义类继承process实现多进程
  1. #方法2
    from multiprocessing import Process
    import time
    import os
    import random
    class Myprocess(Process):
    def __init__(self,func):
    super().__init__()
    self.func = func
  2.  
  3. def run(self):
    self.func()
    def func1():
    print("子进程1测试")
    print("子进程1pid",os.getpid())
  4.  
  5. def func2():
    print("子进程2测试")
    print("子进程2pid2",os.getpid())
  6.  
  7. if __name__ == '__main__':
    p1 = Myprocess(func1)
    p2 = Myprocess(func2)
    p1.start() #调用子进程中的run方法
    p2.start()
    print("主进程pid",os.getpid())
  1. join方法:把父进程卡住,等待子进程结束才执行父进程
  1. import time
    from multiprocessing import Process
    def func(name):
    time.sleep(3)
    print("%s is writing" %name)
  2.  
  3. if __name__ == '__main__':
    p1 = Process(target=func,args=("ivy",))
    p2 = Process(target=func,args=("zoe",))
    p3 = Process(target=func, args=("zoe",))
    # p1.start()
    # p2.start() #主进程发起创建子进程的请求,由操作系统来创建。
    # p1.join() #卡着等子进程结束,卡的是主进程,子进程一直在后台运行
    # p2.join()
  4.  
  5. p_1 = [p1,p2,p3]
    for p in p_1:
    p.start()
  6.  
  7. for p in p_1:
    p.join()
    print("主进程")
  8.  
  9. 进程的常见方法及其说明
  1. import time
    import os
    from multiprocessing import Process
    def func(name):
    time.sleep(3)
    print("%s is writing" %name)
  2.  
  3. if __name__ == '__main__':
    p1 = Process(target=func,args=("ivy",))
    p1.daemon = True #主进程运行完毕,子进程就回收了
    p1.start()
    print(p1.name) #打印进程名字
    print(os.getpid()) #查看当前进程id
    print(os.getppid()) #查看主进程id
    p1.terminate() #杀进程
    print(p1.is_alive()) #查看进程是否存活
    print("主进程")
  4.  
  5. 基于多进程实现socket通信
    服务端:
  1. import socket
    from multiprocessing import Process
    server = socket.socket(socket.AF_INET,type=socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server.bind(("127.0.0.1",8080))
    server.listen(5)
  2.  
  3. def talk(conn,addr):
    while True:
    try:
    msg = conn.recv(1024)
    if not msg:break
    conn.send(msg.upper())
    except Exception:
    break
  4.  
  5. if __name__ == '__main__':
    while True:
    conn,addr = server.accept()
    p = Process(target=talk,args=(conn,addr))
    p.start()
  1. 客户端:
  1. import socket
    client = socket.socket()
    client.connect(("127.0.0.1",8080))
    while True:
    msg = input("客户端说:")
    client.send(msg.encode("utf-8"))
    msg_server = client.recv(1024)
    print(msg_server.decode("utf-8"))

二.多进程爬虫

  1. import requests
    import time
    import os
    from multiprocessing import Process
  2.  
  3. urls = ["http://p1.music.126.net/EAJfo8I22hDJErMR7WyOUQ==/109951162860207008.jpg",
    "http://p0.qhimgs4.com/t01ba9168ef323dfc7a.jpg",
    "http://m.iqiyipic.com/u7/image/20181107/b3/98/uv_20036427021_m_601_720_405.jpg"]
  4.  
  5. def download(url,i):
    time.sleep(1)
    url = requests.get(url)
    new_url = url.content
    with open("image%s.jpg" %(i),mode="wb") as f:
    f.write(new_url)
    print(os.getpid())
  6.  
  7. if __name__ == '__main__':
    start_time = time.time()
    p_l = []
    for i,url in enumerate(urls):
    p = Process(target=download,args=(url,i+1))
    p_l.append(p)
    p.start()
    [p.join() for p in p_l]
    print("主进程")
    end_time = time.time()
    print("执行时间",end_time- start_time)

三.守护进程

守护进程把xx设置成守护进程,当主进程结束后xx也结束

把f1设置成守护进程 沉睡一秒,此时主进程已经结束 f1也就跟着结束了。

守护进程不能再开子进程

  1. import time
    from multiprocessing import Process
    def func1():
    time.sleep(1)
    print("我是func1")
  2.  
  3. def func2():
    print("我是func2")
  4.  
  5. if __name__ == '__main__':
    f1 = Process(target=func1)
    f2 = Process(target=func2)
    f1.daemon = True
    f1.start()
    f2.start()
    f2.join()
    print("我是主进程")

四.进程队列

进程与进程之间的通信需要IPC进制来实现,进程之间通信一般有两种方式,管道和队列,而队列就是基于管道和锁来实现的。加锁的弊端相当于进程变成串行的形式运行,降到了执行效率。优势是保证了数据不错乱。队列的特点是先进先出

队列常用方法:

  1. from multiprocessing import Process,Queue
    q = Queue(5) #里面可以传值,默认代表无限大,队列先进先出 堆栈:先进后出
    q.put("hello")
    q.put("world")
    q.put("hello world")
    #q.put("d",False) #代表队列满了就不能往里面放了等同于 nowait
    q.put("d",timeout=2) #代表等两秒
    #ps 也可以放对象
    print(q.get())
    print(q.get())
    print(q.get())
    print(q.get(block=False)) #gen put一样
    print(q.full()) #判断是否已满
    print(q.empty()) #判断是否为空
    print(q.qsize()) #判断大小

五.进程通信的三种方式

1.IPC队列简单应用(实现数据共享之生产者消费者模型)

2.基于文件

3.Manages模块

虽然进程之间是相互隔离的,但是进程是共享一套操作系统和文件

  1. from multiprocessing import Process
    def work(filename,msg):
    with open(filename,mode="a",encoding="utf-8") as f:
    f.write(msg)
    f.write("\n")
  2.  
  3. if __name__ == '__main__':
    for i in range(5):
    p = Process(target=work,args=("a,txt","进程%s" %str(i)))
    p.start()
  4.  
  5. 第一种通信方式基于IPCQueue模块,生产者消费者模型
    例子1
  1. #生产者消费者模型:为了平衡消费者和生产者的数据,两个进程互不打扰,互相不影响对方。
  2.  
  3. import time
    import random
    from multiprocessing import Process,Queue
  4.  
  5. def consumer(q,name):
    while True:
    time.sleep(random.randint(1,3))
    ret = q.get()
    print("\033[41m消费者%s拿到了%s\033[0m" %(name,ret))
  6.  
  7. def producer(seq,q,name):
    for item in seq:
    time.sleep(random.randint(1, 3))
    q.put(item)
    print('\033[42m生产者%s生产了%s\033[0m' %(name,item))
  8.  
  9. if __name__ == '__main__':
    q = Queue()
    c = Process(target=consumer,args=(q,"ivy"))
    c.start()
  10.  
  11. seq = ["包子%s" %i for i in range(10)]
    producer(seq,q,"厨师1") #主进程充当生产者
    print("主进程")

例2:基于不同子进程做生产者和消费者,如果生产者队列为空则退出

  1. import time
    import random
    from multiprocessing import Process,Queue
  2.  
  3. def consumer(q,name):
    while True:
    time.sleep(random.randint(1,3))
    ret = q.get()
    if ret is None:break
    print("\033[41m消费者%s拿到了%s\033[0m" %(name,ret))
  4.  
  5. def producer(seq,q,name):
    for item in seq:
    time.sleep(random.randint(1, 3))
    q.put(item)
    print('\033[42m生产者%s生产了%s\033[0m' %(name,item))
    q.put(None)
  6.  
  7. if __name__ == '__main__':
    q = Queue()
    c = Process(target=consumer,args=(q,"ivy"))
    c.start()
  8.  
  9. seq = ["包子%s" %i for i in range(10)]
    p = Process(target=producer,args=(seq,q,"厨师1"))
    p.start()
  10.  
  11. print("主进程")

 例3:基于JoinableQueue模块和守护进程实现队列生产者生产一个 消费者消费一个

  1. import time
    import random
    from multiprocessing import Process,JoinableQueue
  2.  
  3. def consumer(q,name):
    while True:
    time.sleep(random.randint(1,3))
    ret = q.get()
    q.task_done()
    # if ret is None:break
    print("\033[41m消费者%s拿到了%s\033[0m" %(name,ret))
  4.  
  5. def producer(seq,q,name):
    for item in seq:
    time.sleep(random.randint(1, 3))
    q.put(item)
    print('\033[42m生产者%s生产了%s\033[0m' %(name,item))
    q.join()
    print("+++++++++++++++>>>")
  6.  
  7. if __name__ == '__main__':
    q = JoinableQueue()
    c = Process(target=consumer,args=(q,"ivy"))
    c.daemon = True #设置守护进程,主进程结束c就结束
    c.start()
  8.  
  9. seq = ["包子%s" %i for i in range(10)]
    p = Process(target=producer,args=(seq,q,"厨师1"))
    p.start()
    p.join() #主进程等待p,p等待c把数据去完,c一旦取完数据p.join就不在阻塞
    #而主进程结束,主进程结束会回收守护进程c,而且c此时也没有存在的必要
  10.  
  11. print("主进程")
  12.  
  13. 第二种中通讯方式基于Manage模块
  1. 1:进程同步多个进程之间一起修改数据
  1. from multiprocessing import Manager,Process
    import os
    def work(d,lst):
    lst.append(os.getpid())
    d[os.getpid()] = os.getpid()
  2.  
  3. if __name__ == '__main__':
    m = Manager()
    lst = m.list(["init"])
    d = m.dict({"name":"Ivy"})
  4.  
  5. p_1 = []
    for i in range(5):
    p = Process(target=work,args=(d,lst))
    p_1.append(p)
    p.start()
    [p.join() for p in p_1]
    print(d)
    print(lst)
  6.  
  7. 基于Manage做数据共享
  1. from multiprocessing import Process,Manager,Lock
  2.  
  3. def work(d,lock):
    with lock:
    d["count"] -=1
  4.  
  5. if __name__ == '__main__':
    lock = Lock()
    m = Manager()
    d = m.dict({'count':100})
    p_l= []
    for i in range(100):
    p = Process(target=work,args=(d,lock))
    p_l.append(p)
    p.start()
    [p.join() for p in p_l]
    print('主进程',d)

六.线程的两种创建方式

线程的概念:一个进程里面执行有一个控制线程,线程是cpu的执行单位,进程只是把一堆资源结合在一起。真正在cpu上调度的是进程里面的线程。多线程是一个进程里面有多个进程。

为什么要用多线程:因为开启进程的时候需要划分地址空间,在这个过程中耗时长。多个活共享一个资源的时候推荐使用多线程,线程比进程更轻量。线程用的是一个进程里面的资源,创建过程比较快。IO密集的时候多线程的优势比较明显,对于cpu密集型多线程并不能体现效果。

python的多线程用不了多核

  1. 线程:一条流水线的执行过程是一个线程,一条流水线必须属于一个车间。一个车间的运行过程就是一个进程
    一个进程内至少有一个线程,进程是一个资源单位,线程才是cpu的执行单位
  2.  
  3. 多线程:一个车间内有多条流水线,多个流水线共享该车间的资源(多线程共享一个进程的资源)
    线程的开销远远小于进程,
  4.  
  5. 为什么是要使用多线程
    1.共享资源
    2.创建开销小
  6.  
  7. 创建方法一:
  1. from threading import Thread
    def work(name):
    print("%s say hello" %name)
  2.  
  3. if __name__ == '__main__':
    t = Thread(target=work,args=("Ivy",))
    t.start()
    print("主线程")
  1. 创建方法二:
  1. from threading import Thread
    class Work(Thread):
    def __init__(self,name):
    super().__init__()
    self.name = name
  2.  
  3. def run(self):
    print("%s say hello" %self.name)
  4.  
  5. if __name__ == '__main__':
    t = Work("Ivy")
    t.start()
  6.  
  7. 基于多线程写socket
    server
  1. from socket import *
    from threading import Thread
  2.  
  3. def server(ip,port):
    s = socket(AF_INET, SOCK_STREAM)
    s.bind((ip,port))
    s.listen(5)
    while True:
    conn,addr = s.accept()
    print("client",addr)
    t = Thread(target=talk,args=(conn,addr))
    t.start()
  4.  
  5. def talk(conn,addr):
    try:
    while True:
    res = conn.recv(1024)
    if not res:break
    print("client %s:%s msg:%s" %(addr[0],addr[1],res))
    conn.send(res.upper())
    except Exception:
    pass
    finally:
    conn.close()
    if __name__ == '__main__':
    server("127.0.0.1",8080)
  6.  
  7. client
  1. from socket import *
    c = socket()
    c.connect(("127.0.0.1",8080))
    while True:
    msg = input(">>: ").strip()
    if not msg:continue
    c.send(msg.encode("utf-8"))
    res = c.recv(1024)
    print("from server msg:" ,res.decode("utf-8"))
  2.  
  3. 线程的常用方法:
  1. import time
    import threading
    from threading import Thread
    def work():
    time.sleep(2)
    print("%s say hello" %threading.current_thread().getName())
  2.  
  3. if __name__ == '__main__':
    t = Thread(target=work)
    # #t.daemon = True
    # t.setDaemon(True)
    t.start()
    print(threading.enumerate()) #查看当前活跃的进程是一个列表
    print(threading.active_count()) #当前活跃的线程数
    print("主进程",threading.current_thread().getName())
  1. 基于多线程实现对文件的格式化保存
  1. from threading import Thread
  2.  
  3. msg_l=[]
    format_l = []
    def talk():
    while True:
    msg = input(">>: ").strip()
    if not msg:continue
    msg_l.append(msg)
  4.  
  5. def format():
    while True:
    if msg_l:
    res = msg_l.pop()
    res = res.upper()
    format_l.append(res)
  6.  
  7. def save():
    while True:
    if format_l:
    res = format_l.pop()
    with open("db.txt","a",encoding="utf-8") as f:
    f.write("%s\n"%res)
  8.  
  9. if __name__ == '__main__':
    t1 = Thread(target=talk)
    t2 = Thread(target=format)
    t3 = Thread(target=save)
    t1.start()
    t2.start()
    t3.start()

七.线程和进程的效率对比

线程与进程的区别

线程共享创建他进程的地址空间,线程可以直接访问里面的数据,线程可以跟他进程里面的线程通信。进程和进程通讯必须使用IPC。线程的创建开启小,主线程可直接控制子线程。进程只能控制子进程,改变子进程不能影响父进程。

python解释器的进程是直接调用操作系统的系统。属于内核级别的进程。

八.线程共享同一进程的数据

  可以通过事件实现数据共享

  1. from threading import Event,Thread
    import threading
    import time
    def conn_mysql():
    print("%s waiting....."%threading.current_thread().getName())
    e.wait()
    print("%s start to connect mysql...." % threading.current_thread().getName())
    time.sleep(2)
  2.  
  3. def check_mysql():
    print("%s checking....." % threading.current_thread().getName())
    time.sleep(4)
    e.set()
  4.  
  5. if __name__ == '__main__':
    e = Event()
    c1 = Thread(target=conn_mysql)
    c2 = Thread(target=conn_mysql)
    c3 = Thread(target=conn_mysql)
    c4 = Thread(target=check_mysql)
    c1.start()
    c2.start()
    c3.start()
    c4.start()

九.加锁和解决死锁现象(互斥锁和递归锁)

死锁案列:

  1. from threading import Thread,Lock
    import time
    class MyThread(Thread):
    def run(self):
    self.f1()
    self.f2()
  2.  
  3. def f1(self):
    mutaxA.acquire()
    print("\033[46m%s拿到A锁\033[0m" %self.name)
    mutaxB.acquire()
    print("\033[43m%s拿到B锁\033[0m" % self.name)
    mutaxB.release()
    mutaxA.release()
  4.  
  5. def f2(self):
    mutaxB.acquire()
    time.sleep(1)
    print("\033[43m%s拿到B锁\033[0m" % self.name)
    mutaxA.acquire()
    print("\033[42m%s拿到A锁\033[0m" % self.name)
    mutaxA.release()
    mutaxB.release()
  6.  
  7. if __name__ == '__main__':
    mutaxA = Lock()
    mutaxB = Lock()
    # t = MyThread()
    # t.start()
    for i in range(20):
    t = MyThread()
    t.start()
  8.  
  9. 基于递归锁来解决:递归锁里面使用的是计算器,遇到锁的时候加1,释放锁减1,只有等到计数器数字为1的时候别人才能拿到锁。
  1. from threading import Thread,Lock,RLock
    import time
    class MyThread(Thread):
    def run(self):
    self.f1()
    self.f2()
  2.  
  3. def f1(self):
    mutaxA.acquire()
    print("\033[46m%s拿到A锁\033[0m" %self.name)
    mutaxB.acquire()
    print("\033[43m%s拿到B锁\033[0m" % self.name)
    mutaxB.release()
    mutaxA.release()
  4.  
  5. def f2(self):
    mutaxB.acquire()
    time.sleep(1)
    print("\033[43m%s拿到B锁\033[0m" % self.name)
    mutaxA.acquire()
    print("\033[42m%s拿到A锁\033[0m" % self.name)
    mutaxA.release()
    mutaxB.release()
  6.  
  7. if __name__ == '__main__':
    mutaxA = mutaxB = RLock()
    # mutaxA = Lock()
    # mutaxB = Lock()
    # t = MyThread()
    # t.start()
    for i in range(20):
    t = MyThread()
    t.start()
  8.  
  9. 信号量锁:相当于同一时间有几个人可以拿锁
  1. from threading import Thread,Semaphore
    import time
    def work(id):
    with sem:
    time.sleep(2)
    print("%s say hello" %id)
  2.  
  3. if __name__ == '__main__':
    sem = Semaphore(5)
    for i in range(20):
    t = Thread(target=work,args=(1,))
    t.start()

例1:以抢票为例加锁

  1. from multiprocessing import Process,Lock
    import json
    import time
    import os
    import random
  2.  
  3. def work(dbfile,name,lock):
    lock.acquire()
    with open(dbfile,encoding="utf-8") as f:
    dic = json.loads(f.read())
  4.  
  5. if dic["count"] > 0:
    dic["count"] -=1
    time.sleep(random.randint(1,3))
    with open(dbfile,"w",encoding="utf-8") as f:
    f.write(json.dumps(dic))
    print("\033[43m%s抢票成功\033[0m" %name)
    else:
    print("\033[45m%s 抢票失败\033[0m" %name)
    lock.release()
  6.  
  7. if __name__ == '__main__':
    lock = Lock()
    p_l = []
    for i in range(100):
    p = Process(target=work,args=("a.txt","用户%s" %i,lock))
    p_l.append(p)
    p.start()
    [p.join() for p in p_l]
    print("主进程")
  8.  
  9. 2:加锁第二种写法上下文管理with
  1. from multiprocessing import Process,Lock
    import json
    import time
    import os
    import random
  2.  
  3. def work(dbfile,name,lock):
    # lock.acquire()
    with lock:
    with open(dbfile,encoding="utf-8") as f:
    dic = json.loads(f.read())
  4.  
  5. if dic["count"] > 0:
    dic["count"] -=1
    time.sleep(random.randint(1,3))
    with open(dbfile,"w",encoding="utf-8") as f:
    f.write(json.dumps(dic))
    print("\033[43m%s抢票成功\033[0m" %name)
    else:
    print("\033[45m%s 抢票失败\033[0m" %name)
    # lock.release()
  6.  
  7. if __name__ == '__main__':
    lock = Lock()
    p_l = []
    for i in range(100):
    p = Process(target=work,args=("a.txt","用户%s" %i,lock))
    p_l.append(p)
    p.start()
    [p.join() for p in p_l]
  8.  
  9. 解决死锁把Lock 换成RLock即可

十.线程队列的三种应用

第一种:先进先出

  1. import queue
    q = queue.Queue(5)
    q.put("hello")
    q.put("world")
    q.put("hello world")
    q.put_nowait("hey")
    print(q.qsize())
  2.  
  3. print(q.get())
    print(q.get())
    print(q.get())
    print(q.empty())
    print(q.full())
    print(q.get_nowait())
  4.  
  5. 第二种先进后出
  1. import queue
    q = queue.LifoQueue(5)
    q.put("a")
    q.put("b")
    q.put_nowait("c")
  2.  
  3. print(q.get())
    print(q.get())
    print(q.get())
  4.  
  5. 第三种指定优先级
    只能指定元组或者列表的形式,数字越小,优先级最大
  1. import queue
    q = queue.PriorityQueue(5)
    q.put((1,"c"))
    q.put((2,"a"))
    q.put((3,"b"))
  2.  
  3. print(q.get())
    print(q.get())
    print(q.get())

十一.多线程执行计算密集型任务

  对于IO密集型来说,使用多进程没有,对于计算密集行使用多进程比较占优势。

一个cpu在同一时间只能处理一个进程里面的线程。原因跟GIL相关。

十二. 线程池和进程池

 进程池:一般开进程可参考cpu的核数

  1. import os
    import time
    import random
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    def func(n):
    time.sleep(random.randint(1,3))
    return n*n
  2.  
  3. if __name__ == '__main__':
    pool = ProcessPoolExecutor(max_workers=5)
    p_lst = []
    for i in range(10):
    ret = pool.submit(func,i) #异步提交任务,func是函数名,i是func函数的参数
    p_lst.append(ret)
    # pool.shutdown() #锁定线程池,不让新任务再提交进来了.轻易不用
    #[i.result() for i in p_lst]
    for i in p_lst:
    print(i.result()) #有join的效果

十三. 回调函数

回调函数方法1:使用Pool模块

  1. import os
    from multiprocessing import Pool,Process
  2.  
  3. def work(n):
    return n*n
  4.  
  5. if __name__ == '__main__':
    pool = Pool(5)
    res_l = []
    for i in range(6):
    res = pool.apply_async(work,args=(i,))
    res_l.append(res)
  6.  
  7. for res in res_l:
    print(res.get()) ps:我也不太明白
  8.  
  9. 回调函数2:基于模块实现
  1. #把一个任务的执行结果给另外一个函数去处理,应用场景爬虫
  2.  
  3. from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    def func1(x,y):
    return x+y
  4.  
  5. def func2(n):
    print(n)
    print(n.result())
  6.  
  7. if __name__ == '__main__':
    pool = ProcessPoolExecutor(max_workers=5,)
    pool.submit(func1,5,10).add_done_callback(func2)
    ps:如果要用线程,创建对象的时候把ProcessPoolExecutor换成ThreadPoolExecutor

十四. 守护线程

  1. 守护线程
  1. import time
    import threading
    from threading import Thread
    def work():
    time.sleep(2)
    print("say hello")
  2.  
  3. if __name__ == '__main__':
    t = Thread(target=work)
    #t.daemon = True
    t.setDaemon(True)
    t.start()
    print("主进程")

十五. 协程

  1. 单线程下的并发,协程 是一种用户态的轻量级线程
    python的线程属于内核级别的
    协程是单线程下的并发,当遇到io是自动切换到别的协程,必须在一个单线程下实现并发,不需要加锁,本质是是串行运行。
    只是切换速度很快。
    要实现协程,主要用户自己控制切换,保存状态。
  1. yield实现两个程序之间快速切换的例子
  1. import time
    def consumer():
    #print(item)
    x = 2222222222222
    y=33333333333333
    a = "aaaaaaaaaaaaaaaaaaaa"
    b = "ccccccccccccccc"
    while True:
    item = yield
  2.  
  3. def producere(target,seq):
    for item in seq:
    target.send(item)
  4.  
  5. g=consumer()
    next(g)
  6.  
  7. start_time = time.time()
    producere(g,range(100000))
    stop_time = time.time()
    print("运行时间",stop_time-start_time)
  1. greenlet模块的switch方法切换
  1. from greenlet import greenlet
  2.  
  3. def test1():
    print("test1,first")
    gr2.switch()
    print("test1,second")
    gr2.switch()
  4.  
  5. def test2():
    print("test2,first")
    gr1.switch()
    print("test2,second")
  6.  
  7. gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()
  1. gevent实现协程
  1. import gevent
  2.  
  3. def eat(name):
    print("%s eat food first" %name)
    gevent.sleep(5)
    print("%s eat food second" % name)
  4.  
  5. def play(name):
    print("%s play phone 1" %name)
    gevent.sleep(10)
    print("%s play phone 1" % name)
  6.  
  7. g1 = gevent.spawn(eat,"ivy")
    g2 = gevent.spawn(play,"zoe")
    g1.join()
    g2.join()
    print("主")
  8.  
  9. 完整版的gevent
  10.  
  11. 打添丁实现的,如果不打补丁不会识别timesleep方法
  1. from gevent import monkey;monkey.patch_all()
    import gevent
    import time
  2.  
  3. def eat(name):
    print("%s eat food first" %name)
    time.sleep(5)
    print("%s eat food second" % name)
  4.  
  5. def play(name):
    print("%s play phone 1" %name)
    time.sleep(10)
    print("%s play phone 1" % name)
  6.  
  7. g1 = gevent.spawn(eat,"ivy")
    g2 = gevent.spawn(play,"zoe")
    g1.join()
    g2.join()
    print("主")
  8.  
  9. 通过gevent实现爬虫
  1. from gevent import monkey; monkey.patch_all()
    import requests
    import time
    import gevent
  2.  
  3. def get_page(url):
    print("get page:%s" %url)
    response = requests.get(url)
    if response.status_code == 200:
    print(response.text)
  4.  
  5. start_time = time.time()
    g1 = gevent.spawn(get_page,url = "https://www.python.org")
    g2 = gevent.spawn(get_page,url="https://yahoo.com")
    g3 = gevent.spawn(get_page,url = "https://github.com")
    gevent.joinall([g1,g2,g3])
    stop_time = time.time()
    print("时长",stop_time-start_time)

十六. GIL全局解释器锁

  1. 只有cpython才有,cpython的线程管理不安全,
    python中同一个进程下开的线程只能有一个cpu执行
    GIL保护的是解释器的数据
    针对不同的数据使用不同的锁去保护
    python解释器调用的是操作系统的原生线程,谁先拿到GIL锁谁先执行,保护共享数据知识补充:1.定时执行任务

知识点补充

  1. 定时去运行一个任务
    from threading import Timer
    def hello(name):
    print("%s say hello" %name)
  2.  
  3. t = Timer(3,hello,args=("Ivy",))
    t.start()

day09 并发编程的更多相关文章

  1. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  2. 伪共享(false sharing),并发编程无声的性能杀手

    在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...

  3. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  4. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  5. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  6. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  7. 【Java并发编程实战】-----“J.U.C”:Exchanger

    前面介绍了三个同步辅助类:CyclicBarrier.Barrier.Phaser,这篇博客介绍最后一个:Exchanger.JDK API是这样介绍的:可以在对中对元素进行配对和交换的线程的同步点. ...

  8. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  9. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

随机推荐

  1. List去重为什么要写equals(),hashCode()方法

    一,各个集合的特点: Collection(集合):容器,用于存放对象(引用类型.基本类型需要自动装箱) List(列表):元素有序,元素可以重复 (有索引). 通过元素的equals()方法判断是否 ...

  2. R语言与概率统计(三) 多元统计分析(下)广义线性回归

    广义线性回归 > life<-data.frame( + X1=c(2.5, 173, 119, 10, 502, 4, 14.4, 2, 40, 6.6, + 21.4, 2.8, 2. ...

  3. delphi treeview的子节点图标?

    代码实现不同的子节点图标效果. unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, ...

  4. ElasticSearch、Logstash管理和监控——blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]

    1.某一天出现Kafka堆积大量未消费的记录: 2.该主题是用logstash进行消费的,然后查询logstash的日志(logstash/logs/logstash-plain.log),出现以下提 ...

  5. —Entity Framework实例详解

    Entity Framework Code First的默认行为是使用一系列约定将POCO类映射到表.然而,有时候,不能也不想遵循这些约定,那就需要重写它们.重写默认约定有两种方式:Data Anno ...

  6. 关键字 using语句 大神的神扯

    using 是非托管资源: 解析:在C#应用托管到.NET Framework.但是他可以释放非托管资源. using 关键字有两个作用: 1:作为关键字,using可以导入命名空间 2:座位C#语句 ...

  7. JS GZIP压缩

    GZIP压缩,GZIP解压需要用到 pako.js 文件:下载地址:https://download.csdn.net/download/qq_35713752/10627338 使用方法: JS压缩 ...

  8. python基础知识(循环语句)

    for循环.while循环.循环嵌套 for 迭代变量 In 对象: 循环体 range(start,end,step) 第一个和第三个可以省略生成一系列的连续整数 start 包括起始值 end  ...

  9. android#嵌入式布局并创建自定义控件

    一.如何在android中嵌入布局文件: 新建一个布局title.xml,该文件为公共文件 <LinearLayout xmlns:android="http://schemas.an ...

  10. Linux系统封装成iso文件

    #安装所需软件包yum -y install createrepo mkisofs anaconda-runtime 根据root下的install.log文件,得到安装的软件包awk '/Insta ...