python之并发编程进阶篇9
一、守护进程和守护线程
1)守护进程的概念
- 什么是守护进程:
- 守护: 在主进程代码结束情况下,就立即死掉
- 守护进程本质就是一个子进程,该子进程守护着主进程
- 为何要用守护进程
- 守护进程本质就是一个子进程,所以在主进程需要将任务并发执行的时候需要开启子进程
- 当该子进程执行的任务生命周期伴随主进程整个生命周期的时候,就需要将该子进程做成守护的进程
2)创建守护进程
- from multiprocessing import Process
- import time
- def task(x):
- print('%s is running' %x)
- time.sleep()
- print('%s is done' % x)
- if __name__ == '__main__':
- p=Process(target=task,args=('守护进程',))
- p.daemon=True # 必须放到p.start()之前
- p.start()
- time.sleep()
- print('主')
3)守护线程的概念
- 主线程要等到该进程内所有非守护线程(子线程)都死掉才算死掉,因为主线程的生命周期
- 代表了该进程的生命周期,该进程一定是要等到所有非守护的线程都干完活才应该死掉
- 可以简单理解为:
- 守护线程是要等待该进程内所有非守护的线程都运行完毕才死掉
4)创建守护线程
- from threading import Thread
- import time
- def task(x):
- print('%s is running' %x)
- time.sleep()
- print('%s is done' % x)
- if __name__ == '__main__':
- t=Thread(target=task,args=('守护线程',))
- t.daemon=True # 必须放到p.start()之前
- t.start()
- print('主')
二、互斥锁和信号量与GIL全局解释器锁,死锁及递归锁
1)互斥锁的意义
- 互斥锁的原理是将进程/线程内执行的部分代码由并发执行变成穿行执行,牺牲了效率但保证数据安全
- 互斥锁不能连续低执行mutex.acquire()操作,必须等到拿着锁的进程释放锁mutex.release()其他进程才能抢到
2)进程mutex=Lock()
- from multiprocessing import Process,Lock
- import json
- import os
- import time
- import random
- mutex=Lock()
- def check():
- with open('db.json','rt',encoding='utf-8') as f:
- dic=json.load(f)
- print('%s 剩余票数:%s' %(os.getpid(),dic['count']))
- def get():
- with open('db.json','rt',encoding='utf-8') as f:
- dic=json.load(f)
- time.sleep()
- if dic['count'] > :
- dic['count']-=
- time.sleep(random.randint(,)) #模拟网络延迟
- with open('db.json','wt',encoding='utf-8') as f:
- json.dump(dic,f)
- print('%s 抢票成功' %os.getpid())
- def task(mutex):
- # 并发查看
- check()
- # 串行购票
- mutex.acquire()
- get()
- mutex.release()
- if __name__ == '__main__':
- for i in range():
- p=Process(target=task,args=(mutex,))
- p.start()
- # p.join() # 将p内的代码变成整体串行
3)信号量。设置能同时执行任务的数量
- # 比如公共厕所,能同时上厕所的有4个位置
- from multiprocessing import Process,Semaphore
- import os
- import time
- import random
- sm=Semaphore()
- def go_wc(sm):
- sm.acquire()
- print('%s is wcing' %os.getpid())
- time.sleep(random.randint(,))
- sm.release()
- if __name__ == '__main__':
- for i in range():
- p=Process(target=go_wc,args=(sm,))
- p.start()
4)线程问题版。线程中修改同一个数据,数据存在不安全,故障
- from threading import Thread
- import time
- n =
- def task():
- global n
- temp = n
- time.sleep(0.1)
- n = temp -
- if __name__ == '__main__':
- t_l = []
- for i in range():
- t = Thread(target=task)
- t_l.append(t)
- t.start()
- for t in t_l:
- t.join()
- print(n)
5)线程互斥锁修改版
- from threading import Thread,Lock
- import time
- mutex = Lock()
- n =
- def task():
- global n
- with mutex: # 拿到锁,自动释放锁
- temp = n
- time.sleep(0.1)
- n = temp -
- if __name__ == '__main__':
- t_l = []
- start_time = time.time()
- for i in range():
- t = Thread(target=task)
- t_l.append(t)
- t.start()
- for t in t_l:
- t.join()
- print(n)
- print(time.time()- start_time)
6)GIL的意义。判断什么情况下使用线程和进程
- GIL是什么
- GIL是全局解释器锁,本质就是一把互斥锁
- GIL是Cpython解释器的特性,而不是python的特性
- 每启动一个进程,该进程就会有一个GIL锁,用来控制该进程内的多个线程同一时间只有一个执行
- 这意味着Cpython解释器的多线程没有并行的效果,但是有并发的效果
- 、为什么要有GIL
- 因为Cpython解释器的垃圾回收机制不是线程安全的
- 、GIL vs 自定义互斥锁
- 在一个进程内的多个线程要想执行,首先需要抢的是GIL,GIL就相当于执行权限
- python的多进程用于计算密集型
- python的多线程用于IO密集型
7)计算密集型中,进程计算和线程计算对比
进程计算,计算密集型。利用多核cpu的优势,但进程数不能超过核数的2倍,会大量消耗cpu资源。计算时间 27.400567293167114
- from multiprocessing import Process
- import time
- def task1():
- res=
- for i in range():
- res*=i
- def task2():
- res =
- for i in range():
- res += i
- def task3():
- res =
- for i in range():
- res -= i
- def task4():
- res =
- for i in range():
- res += i
- if __name__ == '__main__':
- start_time=time.time()
- p1=Process(target=task1)
- p2=Process(target=task2)
- p3=Process(target=task3)
- p4=Process(target=task4)
- p1.start()
- p2.start()
- p3.start()
- p4.start()
- p1.join()
- p2.join()
- p3.join()
- p4.join()
- stop_time=time.time()
- print(stop_time-start_time) #27.400567293167114
计算密集型任务用多进程
线程进行密集计算。计算时间 86.84396719932556
- import time
- from threading import Thread
- def task1():
- res=
- for i in range():
- res*=i
- def task2():
- res =
- for i in range():
- res += i
- def task3():
- res =
- for i in range():
- res -= i
- def task4():
- res =
- for i in range():
- res += i
- if __name__ == '__main__':
- start_time=time.time()
- p1=Thread(target=task1)
- p2=Thread(target=task2)
- p3=Thread(target=task3)
- p4=Thread(target=task4)
- p1.start()
- p2.start()
- p3.start()
- p4.start()
- p1.join()
- p2.join()
- p3.join()
- p4.join()
- stop_time=time.time()
- print(stop_time-start_time) # 86.84396719932556
8)IO密集型中。进程与线程对比
进程完成时间 3.5172011852264404
- import time
- from multiprocessing import Process
- def task1():
- time.sleep()
- def task2():
- time.sleep()
- def task3():
- time.sleep()
- def task4():
- time.sleep()
- if __name__ == '__main__':
- start_time=time.time()
- p1=Process(target=task1)
- p2=Process(target=task2)
- p3=Process(target=task3)
- p4=Process(target=task4)
- p1.start()
- p2.start()
- p3.start()
- p4.start()
- p1.join()
- p2.join()
- p3.join()
- p4.join()
- stop_time=time.time()
- print(stop_time-start_time) # 3.5172011852264404
线程优势,完成时间 3.003171443939209
- import time
- from threading import Thread
- def task1():
- time.sleep()
- def task2():
- time.sleep()
- def task3():
- time.sleep()
- def task4():
- time.sleep()
- if __name__ == '__main__':
- start_time=time.time()
- p1=Thread(target=task1)
- p2=Thread(target=task2)
- p3=Thread(target=task3)
- p4=Thread(target=task4)
- p1.start()
- p2.start()
- p3.start()
- p4.start()
- p1.join()
- p2.join()
- p3.join()
- p4.join()
- stop_time=time.time()
- print(stop_time-start_time) # 3.003171443939209
9)死锁现象。释放锁之前,都需要获取到对方的锁,造成了无法释放锁
- from threading import Thread,Lock
- import time
- mutexA = Lock()
- mutexB = Lock()
- class Mythread(Thread):
- def run(self):
- self.f1()
- self.f2()
- def f1(self):
- mutexA.acquire()
- print('%s 抢到了A锁' %self.name)
- mutexB.acquire()
- print('%s 抢到了B锁' % self.name)
- mutexB.release()
- mutexA.release()
- def f2(self):
- mutexB.acquire()
- print('%s 抢到了B锁' % self.name)
- time.sleep()
- mutexA.acquire()
- print('%s 抢到了A锁' % self.name)
- mutexA.release()
- mutexB.release()
- if __name__ == '__main__':
- for i in range():
- t = Mythread()
- t.start()
10)递归锁。RLock,解决死锁现象。递归锁可以连续acquire()
- from threading import Thread,RLock
- import time
- mutexA = mutexB = RLock()
- class Mythread(Thread):
- def run(self):
- self.f1()
- self.f2()
- def f1(self):
- mutexA.acquire()
- print('%s 抢到了A锁' %self.name)
- mutexB.acquire()
- print('%s 抢到了B锁' % self.name)
- mutexB.release()
- mutexA.release()
- def f2(self):
- mutexB.acquire()
- print('%s 抢到了B锁' % self.name)
- time.sleep()
- mutexA.acquire()
- print('%s 抢到了A锁' % self.name)
- mutexA.release()
- mutexB.release()
- if __name__ == '__main__':
- for i in range():
- t = Mythread()
- t.start()
三、IPC机制或队列和生产者模型
IPC:进程间通信,有两种解决方案:队列、管道
1)队列,先进先出。应用于生产者模型
- from multiprocessing import Queue
- q=Queue(maxsize=)
- q.put({'x':})
- q.put()
- q.put('third')
- print(q.get())
- print(q.get())
- print(q.get())
默认不加参数,超过队列最大值会堵塞。 q.put(1,block=False) 超过最大值,程序中断。效果等同于 q.put_nowait(1)。
timeout=3 超时时间,block=True的时候,才有意义
2)生产者模型的意义
- 、什么是生产者消费者模型
- 生产者消费者模型指的是一种解决问题的思路
- 该模型中包含两类明确的角色:
- 、生产者:创造数据的任务
- 、消费者:处理数据的任务
- 、为什么要用生产者消费者模型?
- 、实现生产者与消费者任务的解耦和
- 、平衡了生产者的生产力与消费者消费力
- 一旦程序中出现明显的两类需要并发执行的任务,一类是负责数据的,另外一类是负责处理数据的
- 那么就可以使用生产者消费者模型来提升执行效率
- 、如何用
- 生产者----》队列《-------消费者
- 队列
- 、队列占用的是内存控制,即便是不指定队列的大小也不可能无限制地放数据
- 、队列是用来传递消息的介质,即队列内存放的是数据量较小的数据
2)生产者模型Queue,消费者卡住的不完善版本
- from multiprocessing import Queue,Process
- import time
- def producer(name,q):
- for i in range():
- res = '包子%s' %i
- time.sleep(0.5)
- print('\033[45m厨师%s 生成了%s\033[0m' %(name,res))
- q.put(res)
- def consumer(name,q):
- while True:
- res = q.get()
- time.sleep()
- print('\033[47m吃货%s 吃了%s\033[0m'%(name,res))
- if __name__ == '__main__':
- q = Queue()
- # 生产者们
- p1 = Process(target=producer,args=('egon',q))
- # 消费者们
- c1 = Process(target=consumer,args=('alex',q))
- p1.start()
- c1.start()
- print("主")
3)low版生产者模型Queue,结束信号None
- from multiprocessing import Queue,Process
- import time
- def producer(name,food,q):
- for i in range():
- res = '%s%s' %(food,i)
- time.sleep(0.5)
- print('\033[45m厨师%s 生成了%s\033[0m' %(name,res))
- q.put(res)
- def consumer(name,q):
- while True:
- res = q.get()
- time.sleep()
- print('\033[47m吃货%s 吃了%s\033[0m'%(name,res))
- if __name__ == '__main__':
- q = Queue()
- # 生产者们
- p1 = Process(target=producer, args=('egon','蛋糕',q))
- p2 = Process(target=producer, args=('lxx','面包' ,q))
- p3 = Process(target=producer, args=('cxx','炸弹' ,q))
- # 消费者们
- c1 = Process(target=consumer,args=('alex',q))
- c2 = Process(target=consumer, args=('wcc', q))
- p1.start()
- pfile:/D:/oldboyedu/manth-/day-/tet2.start()
- p3.start()
- c1.start()
- c2.start()
- p1.join()
- p2.join()
- p3.join()
- q.put(None)
- q.put(None)
- print('主')
- print("主")
4)生产者模型最终版本 JoinableQueue,以守护进程的方式来结束
- from multiprocessing import JoinableQueue,Process
- import time
- def producer(name,food,q):
- for i in range():
- res = '%s%s' %(food,i)
- time.sleep(0.5)
- print('\033[45m厨师%s 生成了%s\033[0m' %(name,res))
- q.put(res)
- def consumer(name,q):
- while True:
- res = q.get()
- time.sleep()
- print('\033[47m吃货%s 吃了%s\033[0m'%(name,res))
- q.task_done() # 消费者拿了一个,队列就少了一个
- if __name__ == '__main__':
- q = JoinableQueue()
- # 生产者们
- p1 = Process(target=producer, args=('egon','蛋糕',q))
- p2 = Process(target=producer, args=('lxx','面包' ,q))
- p3 = Process(target=producer, args=('cxx','炸弹' ,q))
- # 消费者们
- c1 = Process(target=consumer,args=('alex',q))
- c2 = Process(target=consumer, args=('wcc', q))
- c1.daemon = True
- c2.daemon = True
- p1.start()
- p2.start()
- p3.start()
- c1.start()
- c2.start()
- p1.join()
- p2.join()
- p3.join() # 此时 3个生产者都已经生产完了
- q.join() # 、证明生产者都已经完全生产完毕 、队列为空,也就是消费者也消费完毕
- print('主')
四、线程queue
1)队列:先进先出
- import queue
- q=queue.Queue()
- q.put()
- q.put()
- q.put()
- print(q.get())
- print(q.get())
- print(q.get())
队列
2)堆栈:先进后出
- import queue
- q=queue.LifoQueue()
- q.put()
- q.put()
- q.put()
- print(q.get())
- print(q.get())
- print(q.get())
堆栈
3)优先级队列:优先级高的优先出来
- import queue
- q=queue.PriorityQueue()
- q.put((,'lxx'))
- q.put((,'egon')) #数字代表优先级,数字越小优先级越高
- q.put((,'alex'))
- print(q.get())
- print(q.get())
- print(q.get())
优先级队列
五、进程池与线程池
1)池的概念
- 、什么进程池、线程池
- 池指的一个容器,该容器用来存放进程或线程,存放的数目是一定的
- 、为什么要用池
- 用池是为了将并发的进程或线程数目控制在计算机可承受的范围内
- 为何要用进程进池?
- 当任务是计算密集型的情况下应该用进程来利用多核优势
- 为何要用线程进池?
- 当任务是IO密集型的情况下应该用线程减少开销
2)同步与异步
- 同步调用 vs 异步调用
- 异步调用与同步调用指的是提交任务的两种方式
- 同步调用:提交完任务后,就在原地等待任务执行完毕,拿到运行结果/返回值后再执行下一行代码
- 同步调用下任务的执行是串行执行
- 异步调用:提交完任务后,不会原地等待任务执行完毕,结果 futrue = p.submit(task,i),结果记录在内存中, 直接执行下一行代码
- 同步调用下任务的执行是并发执行
3)异步进程池
- from concurrent.futures import ProcessPoolExecutor
- import os
- import time
- import random
- def task(x):
- print('%s is running' %os.getpid())
- time.sleep(random.randint(,))
- return x**
- if __name__ == '__main__':
- p=ProcessPoolExecutor() #不指定参数默认池的大写等于cpu的核数
- futrues = [] # 保存任务返回值
- for i in range():
- futrue = p.submit(task,i) # 提交任务,异步提交
- futrues.append(futrue) # 保存任务返回值
- p.shutdown(wait=True) # 关闭了继续提入口交任务的,wait=True 把进程池的里的事做完,再执行后面的任务
- for futrue in futrues:
- print(futrue.result()) # 输入返回结果值
- print('主')
4)同步进程池
- from concurrent.futures import ProcessPoolExecutor
- import os
- import time
- import random
- def task(x):
- print('%s is running' %os.getpid())
- time.sleep(random.randint(,))
- return x**
- if __name__ == '__main__':
- p=ProcessPoolExecutor() #不指定参数默认池的大写等于cpu的核数
- for i in range():
- res = p.submit(task,i).result() # 提交任务,异步提交
- print(res)
- print('主')
小结,同步与异步,对于获取任务返回值的方式,在于什么时候 obj.result()。
6)回调函数,进程池,解析任务返回值。add_done_callback(parse)
- from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
- import os
- import time
- import requests
- def get(url):
- print('%s GET %s' %(os.getpid(),url))
- time.sleep()
- response=requests.get(url)
- if response.status_code == :
- res=response.text
- return res
- def parse(obj):
- res = obj.result()
- print('%s 解析[url]结果是 %s' % (os.getpid(), len(res)))
- if __name__ == '__main__':
- p=ProcessPoolExecutor()
- urls=[
- 'https://www.baidu.com',
- 'https://www.python.org',
- 'https://www.openstack.org',
- 'https://www.taobao.com',
- 'https://www.jd.com',
- ]
- for url in urls:
- p.submit(get,url).add_done_callback(parse) # 回调函数会在任务运行完毕后自动触发,并且接收该任务对象
- print('主',os.getpid())
7)回调函数,线程池,解析任务返回值。add_done_callback(parse)
- from concurrent.futures import ThreadPoolExecutor
- from threading import current_thread
- import time
- import random
- def task(x):
- print('%s is running' %current_thread().getName())
- time.sleep(random.randint(,))
- return x**
- def parse(obj):
- res=obj.result()
- print('%s 解析的结果为%s' %(current_thread().getName(),res))
- if __name__ == '__main__':
- t=ThreadPoolExecutor()
- for i in range():
- t.submit(task,i).add_done_callback(parse)
六、补充知识
1)线程event事件。一个线程发了一个信号,另外个线程收到该信号,才能继续执行
- from threading import Event,current_thread,Thread
- import time
- event=Event() # 生成信号事件
- def check():
- print('%s 正在检测服务是否正常....' %current_thread().name)
- time.sleep()
- event.set() # 发送信号
- def connect():
- print('%s 等待连接...' %current_thread().name)
- event.wait() # 接收信号
- print('%s 开始连接...' % current_thread().name)
- if __name__ == '__main__':
- t1=Thread(target=connect)
- t2=Thread(target=connect)
- t3=Thread(target=connect)
- c1=Thread(target=check)
- t1.start()
- t2.start()
- t3.start()
- c1.start()
2)基于上面内容,设置尝试次数
- from threading import Event,current_thread,Thread
- import time
- event=Event()
- def check():
- print('%s 正在检测服务是否正常....' %current_thread().name)
- time.sleep()
- event.set()
- def connect():
- count=
- while not event.is_set():
- if count == :
- print('尝试的次数过多,请稍后重试')
- return
- print('%s 尝试第%s次连接...' %(current_thread().name,count))
- event.wait()
- count+=
- print('%s 开始连接...' % current_thread().name)
- if __name__ == '__main__':
- t1=Thread(target=connect)
- t2=Thread(target=connect)
- t3=Thread(target=connect)
- c1=Thread(target=check)
- t1.start()
- t2.start()
- t3.start()
- c1.start()
七)协程介绍(单线程下并发)
- 单线程下实现并发:协程
- 并发指的多个任务看起来是同时运行的
- 并发实现的本质:切换+保存状态
- 并发、并行、串行:
- 并发:看起来是同时运行,切换+保存状态
- 并行:真正意义上的同时运行,只有在多cpu的情况下才能
- 实现并行,4个cpu能够并行4个任务
- 串行:一个人完完整整地执行完毕才运行下一个任务
- 实现方法:
- 基于yield保存状态,实现两个任务直接来回切换,即并发的效果
- PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
1)gevent模拟单线程并发(协程),from gevent import monkey;monkey.patch_all(),监控IO,实现单线程的并发操作
- from gevent import monkey;monkey.patch_all()
- import gevent
- import time
- def eat(name):
- print('%s eat 1' %name)
- time.sleep()
- print('%s eat 2' % name)
- def play(name):
- print('%s play 1' %name)
- time.sleep()
- print('%s play 2' % name)
- g1 = gevent.spawn(eat,'egon')
- g2 = gevent.spawn(play,'alex')
- gevent.joinall([g1,g2])
2)DummyThread(单线程的并发:spawn,实现的是假线程)
- from gevent import monkey;monkey.patch_all()
- from threading import current_thread
- import gevent
- import time
- def eat():
- print('%s eat 1' %current_thread().name)
- time.sleep()
- print('%s eat 2' %current_thread().name)
- def play():
- print('%s play 1' %current_thread().name)
- time.sleep()
- print('%s play 2' % current_thread().name)
- g1 = gevent.spawn(eat)
- g2 = gevent.spawn(play)
- print(current_thread().name)
- gevent.joinall([g1,g2])
3)socket连接,单线程下的并发,测试连接抗压能力
- from gevent import monkey,spawn;monkey.patch_all()
- from threading import Thread
- from socket import *
- def talk(conn):
- while True:
- try:
- data=conn.recv()
- if not data:break
- conn.send(data.upper())
- except ConnectionResetError:
- break
- conn.close()
- def server(ip,port,backlog=):
- s = socket()
- s.bind((ip,port))
- s.listen(backlog)
- while True:
- conn, addr = s.accept()
- print(addr)
- # 通信
- g=spawn(talk,conn)
- s.close()
- if __name__ == '__main__':
- spawn(server,'127.0.0.1',).join()
- # server(('127.0.0.1',))
server_spawn
- from threading import Thread,current_thread
- from socket import *
- import os
- def client():
- client = socket()
- client.connect(('127.0.0.1', ))
- while True:
- data = '%s hello' % current_thread().name
- client.send(data.encode('utf-8'))
- res = client.recv()
- print(res.decode('utf-8'))
- if __name__ == '__main__':
- for i in range():
- t=Thread(target=client)
- t.start()
client_Thread
4)网络IO非堵塞模型,实现单线程的并发。s.setblocking(False),与from gevent import monkey;monkey.patch_all()的原理一样
- from socket import *
- s = socket()
- s.bind(('127.0.0.1',))
- s.listen()
- s.setblocking(False)
- r_list=[]
- while True:
- try:
- conn, addr = s.accept()
- r_list.append(conn)
- except BlockingIOError:
- print('可以去干其他的活了')
- print('rlist: ',len(r_list))
- for conn in r_list:
- try:
- data=conn.recv()
- conn.send(data.upper())
- except BlockingIOError:
- continue
socker_server
- from socket import *
- import os
- client = socket()
- client.connect(('127.0.0.1', ))
- while True:
- data='%s say hello' %os.getpid()
- client.send(data.encode('utf-8'))
- res=client.recv()
- print(res.decode('utf-8'))
socker_client
5)网络IO非堵塞模型,修正版,收消息与发消息区分开
- from socket import *
- s = socket()
- s.bind(('127.0.0.1',))
- s.listen()
- s.setblocking(False)
- r_list=[]
- w_list=[]
- while True:
- try:
- conn, addr = s.accept()
- r_list.append(conn)
- except BlockingIOError:
- print('可以去干其他的活了')
- print('rlist: ',len(r_list))
- # 收消息
- del_rlist=[]
- for conn in r_list:
- try:
- data=conn.recv()
- if not data:
- conn.close()
- del_rlist.append(conn)
- continue
- w_list.append((conn,data.upper()))
- except BlockingIOError:
- continue
- except ConnectionResetError:
- conn.close()
- del_rlist.append(conn)
- # 发消息
- del_wlist=[]
- for item in w_list:
- try:
- conn=item[]
- res=item[]
- conn.send(res)
- del_wlist.append(item)
- except BlockingIOError:
- continue
- except ConnectionResetError:
- conn.close()
- del_wlist.append(item)
- # 回收无用连接
- for conn in del_rlist:
- r_list.remove(conn)
- for item in del_wlist:
- w_list.remove(item)
服务端
- from socket import *
- import os
- client = socket()
- client.connect(('127.0.0.1', ))
- while True:
- data='%s say hello' %os.getpid()
- client.send(data.encode('utf-8'))
- res=client.recv()
- print(res.decode('utf-8'))
客户端
6)IO多路复用。select模块优化上面的内容
- from socket import *
- import select
- s = socket()
- s.bind(('127.0.0.1',))
- s.listen()
- s.setblocking(False)
- # print(s)
- r_list=[s,]
- w_list=[]
- w_data={}
- while True:
- print('被检测r_list: ',len(r_list))
- print('被检测w_list: ',len(w_list))
- rl,wl,xl=select.select(r_list,w_list,[],) #r_list=[server,conn]
- # print('rl: ',len(rl)) #rl=[conn,]
- # print('wl: ',len(wl))
- # 收消息
- for r in rl: #r=conn
- if r == s:
- conn,addr=r.accept()
- r_list.append(conn)
- else:
- try:
- data=r.recv()
- if not data:
- r.close()
- r_list.remove(r)
- continue
- # r.send(data.upper())
- w_list.append(r)
- w_data[r]=data.upper()
- except ConnectionResetError:
- r.close()
- r_list.remove(r)
- continue
- # 发消息
- for w in wl:
- w.send(w_data[w])
- w_list.remove(w)
- w_data.pop(w)
server
- from socket import *
- import os
- client = socket()
- client.connect(('127.0.0.1', ))
- while True:
- data='%s say hello' %os.getpid()
- client.send(data.encode('utf-8'))
- res=client.recv()
- print(res.decode('utf-8'))
client
八、目前知识总结,项目篇
python之并发编程进阶篇9的更多相关文章
- python之并发编程初级篇8
一.进程理论 1)进程介绍 .什么是进程 一个正在进行的过程,或者说是一个程序的运行过程 其实进程是对正在运行的程序的一种抽象/概括的说法 进程的概念起源操作系统,进程是操作最核心的概念之一 操作系统 ...
- Python并发编程理论篇
Python并发编程理论篇 前言 其实关于Python的并发编程是比较难写的一章,因为涉及到的知识很复杂并且理论偏多,所以在这里我尽量的用一些非常简明的语言来尽可能的将它描述清楚,在学习之前首先要记住 ...
- 【转】Shell编程进阶篇(完结)
[转]Shell编程进阶篇(完结) 1.1 for循环语句 在计算机科学中,for循环(英语:for loop)是一种编程语言的迭代陈述,能够让程式码反复的执行. 它跟其他的循环,如while循环,最 ...
- Python 3 并发编程多进程之进程同步(锁)
Python 3 并发编程多进程之进程同步(锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,竞争带来的结果就是错乱,如何控制,就是加锁处理. 1. ...
- Python 3 并发编程多进程之守护进程
Python 3 并发编程多进程之守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemo ...
- Python 3 并发编程多进程之队列(推荐使用)
Python 3 并发编程多进程之队列(推荐使用) 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 可以往 ...
- Python 的并发编程
这篇文章将讲解 Python 并发编程的基本操作.并发和并行是对孪生兄弟,概念经常混淆.并发是指能够多任务处理,并行则是是能够同时多任务处理.Erlang 之父 Joe Armstrong 有一张非常 ...
- Python基础—面向对象(进阶篇)
通过上一篇博客我们已经对面向对象有所了解,下面我们先回顾一下上篇文章介绍的内容: 上篇博客地址:http://www.cnblogs.com/phennry/p/5606718.html 面向对象是一 ...
- python之并发编程
一 背景知识 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所 ...
随机推荐
- 由于html元素加载导致的问题
js中要求执行的事件是在完全加载完,但由于本地环境测试一直没发现出问题,在上线后由于网络延迟导致元素加载慢,而事件执行完,没达到预期目标. 这时就需要用到属性 readyState readyStat ...
- LibreOJ 6282. 数列分块入门 6
题目链接:https://loj.ac/problem/6282 参考博客:http://www.cnblogs.com/stxy-ferryman/p/8560551.html 这里如果用数组的话元 ...
- js前端导出excel:json形式的导出
第一中形式的导出:主要是表头对应主体数据,json形式的导出 js库文件名称 : table2excel.js这个js库文件是网上找的,并且自己根据自己业务需求把内容改了一下复制到 table2exc ...
- POJ-1321.棋盘问题.(回溯)
做完题之后看了网上的一些题解但是发现他们的解释大部分都是错误的,所以就自己写了一下,笔者能力也有限,有错误之处大家多多指正. 第一次看题的时候以为就是简单的八皇后,但是写了之后发现存在很多问题,比如需 ...
- linux同步机制
很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本 ...
- f5故障排除
1.硬件问题 1)硬盘:查看/var/log/daemon, kern日志,smartctl测试,EUD 2)PSU: 查看LCD报警,/var/log/ltm,EUD等 3)内存:可能导致设备突然重 ...
- 分布式大数据多维数据分析(olap)引擎kylin[转]
Apache Kylin是一个开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由eBay 开发并贡献至开源社区.它能在亚秒内查询巨大的Hiv ...
- Aria2GUI 导出下载 刷新界面,任务消失
问题1. 2.勾选之后 导出下载 没了,神烦 解决方法解决方法1.点击分享, 2.创建链接 3.然后复制链接到网站, 下面是 下载 工具和 谷歌插件http://www.sdifen.com/ari ...
- linux命令学习之:du
du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 语法 du [选项][文件] 选项 -a或-all 显示目录中个 ...
- call指令
CPU执行call指令时,进行两步操作: 将当前的IP或CS和IP压入栈中; 转移. call指令不能实现短转移,除此之外,call指令实现转移的方法和jmp指令的原理相同. 1)依据位移进行转移的c ...