进程队列补充、socket实现服务器并发、线程完结
1.队列补充
队列内部是管道+锁(数据在队列中是阻塞的)
2.关于python并发与并行的补充
解释型语言单个进程下多个线程不可以并行,但是向C语言等其他语言中在多核情况下是可以实现并行的,所有语言在单核下都是无法实现并行的,只能并发。
3.TCP服务端实现并发
#服务端
import socket
from threading import Thread
server = socket.socket()
server.bind(('127.0.0.1',6666))
server.listen(5)
def serv(conn,addr):
while True:
try:
print(addr)
rec_data = conn.recv(1024).decode('utf-8')
print(rec_data)
send_data = rec_data.upper()
conn.send(send_data.encode('utf-8'))
except Exception as e:
print(e)
break
while True:
conn,addr = server.accept()
t = Thread(target=serv,args=(conn,addr))
t.start()
#客户端
import socket
import time
client = socket.socket()
client.connect(('127.0.0.1',6666))
while True:
client.send(b'hello')
data = client.recv(1024)
print(data)
time.sleep(1)
4.GIL全局解释器锁
在CPython中,全局解释器锁(即GIL)是一个互斥锁,可以防止一个进程中的多个线程同时(并行)执行。 锁定是必要的,主要是因为CPython的内存管理不是线程安全的。GIL的存在就是为了保证线程安全。
什么是保证线程安全呢?
同一进程的所有线程都运行在一个进程内,毫无疑问这些线程具有以下几个特点:
1、所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码)
例如:test.py定义一个函数work(代码内容如下图),在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问到意味着就是可以执行。
2、所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。
综上:
如果多个线程的target=work,那么执行流程是:
多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行
解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码。
GIL与Lock
机智的同学可能会问到这个问题:Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock?
首先,我们需要达成共识:锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据
然后,我们可以得出结论:保护不同的数据就应该加不同的锁。
最后,问题就很明朗了,GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock,如下图
注意:多个线程过来执行,一旦遇到IO操作就会立马释放GIL解释器锁,交个下一个先进来的线程。
在纯计算程序中GIL锁起到锁定python解释器的作用,就是一个线程抢到解释器后不会再有其他线程抢到解释器的使用权(直到这个程序遇到IO操作,这时GIL会释放解释器的使用权)。
import time
from threading import Thread,current_thread
number = 100
def task():
global number
number2 = number
number = number2 - 1
print(number,current_thread().name)
for line in range(100):
t = Thread(target=task)
t.start()
99 Thread-1
98 Thread-2
97 Thread-3
96 Thread-4
95 Thread-5
94 Thread-6
93 Thread-7
92 Thread-8
91 Thread-9
90 Thread-10
。
。
。
给这个程序加上IO操作看下打印结果:
import time
from threading import Thread,current_thread
number = 100
def task():
global number
number2 = number
#print(number)
time.sleep(1)
number = number2 - 1
#time.sleep(1)#如果sleep放在这里,则会打印结果都是0,这是因为线程间数据是共享的
print(number,current_thread().name)
for line in range(100):
t = Thread(target=task)
t.start()
99 Thread-24
99 Thread-23
99 Thread-22
99 Thread-20
99 Thread-18
99 Thread-17
99 Thread-15
99 Thread-21
99 Thread-14
.
.
.
5.验证多线程的作用
什么时候使用多线程,什么时候使用多进程,多线程和多进程各有什么优缺点?
结论:在计算密集型程序中使用多进程,这时能够充分发挥计算机多核的优势;在IO密集型的程序中使用多线程,这时能够充分发挥多线程对CPU高校利用率的优势。高效执行多个进程,内有多个IO密集型程序,要使用多进程+多线程。
对结论的验证:
运算密集型操作验证:
from threading import Thread
from multiprocessing import Process
import os,time
#计算密集型多进程下运行
def work1():
number = 0
for line in range(50000000):
number += 1
if __name__ == '__main__':
#测试计算密集型
print(os.cpu_count())#打印CPU有几个内核
start_time = time.time()
list1 = []
for line in range(4):
p = Process(target=work1)
list1.append(p)
p.start()
#p.join()#join如果放在这里比在列表里执行速度慢,是因为在列表里是等所有的进程都起来之后再告诉系统要加join,而在第一个for循环里面则是每起一个进程都会告诉系统一次,这个过程需要时间。
for p in list1:
p.join()
end_time = time.time()
print(f'程序执行的时间{end_time - start_time}')
#12.24461030960083
#多线程下运行
from threading import Thread
from multiprocessing import Process
import os,time
#计算密集型
def work1():
number = 0
for line in range(50000000):
number += 1
#IO密集型
def work2():
time.sleep(1)
if __name__ == '__main__':
#测试计算密集型
print(os.cpu_count())#打印CPU有几个内核
start_time = time.time()
list1 = []
for line in range(4):
p = Thread(target=work1)
list1.append(p)
p.start()
#p.join()#4.407487869262695
for p in list1:
p.join()
end_time = time.time()
print(f'程序执行的时间{end_time - start_time}')
#16.305360794067383
这里本人的测试结果是在计算的数据比较大时开启多进程才会有优势,如果运算数据比较小,开启多线程运算速度反而比开启多进程快得多。
IO密集型操作验证:
#多进程测试
from threading import Thread
from multiprocessing import Process
import os,time
#IO密集型
def work2():
time.sleep(1)
if __name__ == '__main__':
#测试IO密集型
print(os.cpu_count())#打印CPU有几个内核
start_time = time.time()
list1 = []
for line in range(4):
p = Process(target=work2)
list1.append(p)
p.start()
#p.join()#4.407487869262695
for p in list1:
p.join()
end_time = time.time()
print(f'程序执行的时间{end_time - start_time}')
#2.642327070236206
#多线程测试
from threading import Thread
from multiprocessing import Process
import os,time
#IO密集型
def work2():
time.sleep(1)
if __name__ == '__main__':
#测试IO密集型
print(os.cpu_count())#打印CPU有几个内核
start_time = time.time()
list1 = []
for line in range(4):
p = Thread(target=work2)
list1.append(p)
p.start()
#p.join()#4.407487869262695
for p in list1:
p.join()
end_time = time.time()
print(f'程序执行的时间{end_time - start_time}')
#1.0189073085784912
可以看出在IO密集型操作中,开相同数量的程要比开相同数量的线程执行速度慢
6.死锁现象
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁。
from threading import Lock,Thread,current_thread
import time
mutex_a = Lock()#实例化一把锁
mutex_b = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutex_a.acquire()
print(f'用户{self.name}抢到了锁a')
mutex_b.acquire()
print(f'用户{self.name}抢到了锁b')
mutex_b.release()
print(f'用户{self.name}释放了锁b')
mutex_a.release()
print(f'用户{self.name}释放了锁a')
def func2(self):
mutex_b.acquire()
print(f'用户{self.name}抢到了锁b')
time.sleep(1)
mutex_a.acquire()
print(f'用户{self.name}抢到了锁a')
mutex_a.release()
print(f'用户{self.name}释放了锁a')
mutex_b.release()
print(f'用户{self.name}释放锁b')
for line in range(10):
t = MyThread()
t.start()
用户Thread-1抢到了锁a
用户Thread-1抢到了锁b
用户Thread-1释放了锁b
用户Thread-1释放了锁a
用户Thread-1抢到了锁b
用户Thread-2抢到了锁a
当线程1抢到了b而线程2抢到了a时,线程1要执行强a的任务,而线程2要执行抢b的任务,而两把锁都没有释放手中的锁,所以就造成了死锁的现象。
7.递归锁
递归锁用于解决死锁问题,递归锁可以被多个线程使用,当第一个线程使用时,遇到几把锁,它的引用计数就为几,只有当它的引用计数为零时才会给第二个线程使用。这样拥有递归锁的那个线程就可以将自己的锁里的内容执行完然后,其他使用递归锁的线程才可以执行。也就是但是第一个使用这把锁的线程会对这把锁加一个引用计数,只有引用计数为零时才能真正释放该锁。
from threading import Lock,Thread,RLock
import time
mutex_a = mutex_b = RLock()#实例化一把递归锁
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutex_a.acquire()
print(f'用户{self.name}抢到了锁a')
mutex_b.acquire()
print(f'用户{self.name}抢到了锁b')
mutex_b.release()
print(f'用户{self.name}释放了锁b')
mutex_a.release()
print(f'用户{self.name}释放了锁a')
def func2(self):
mutex_b.acquire()
print(f'用户{self.name}抢到了锁b')
time.sleep(0.1)
mutex_a.acquire()
print(f'用户{self.name}抢到了锁a')
mutex_a.release()
print(f'用户{self.name}释放了锁a')
mutex_b.release()
print(f'用户{self.name}释放锁b')
for line in range(10):
t = MyThread()
t.start()
8.信号量(了解)
互斥锁比喻成一个家用马桶,同一时间只能一个人用;信号量比喻成一个公厕,同一时间可以有多个人用。
from threading import Semaphore,Lock,current_thread,Thread
import time
sm = Semaphore(5)#5个马桶
#mutex = Lock()#一个马桶
def task():
sm.acquire()
print(f'{current_thread().name}执行任务')
time.sleep(1)
sm.release()
for line in range(20):
t = Thread(target=task)
t.start()
#这段代码的功能是每次让五个线程并发执行
9.线程队列
线程Q:线程队列 FIFO(先进先出)就是队列,面试会问。
queue 是python解释器自带的模块,进程中的Queue是python解释器自带的模块multiprocessing里面的一个类。
普通队列(FIFO):先进先出
特殊队列(LIFO):后进先出
import queue
q = queue.Queue()#先进先出
q.put(1)
q.put(2)
print('q',q.get())
q1 = queue.LifoQueue()#先进后出
q1.put(1)
q1.put(2)
print('q1',q1.get())
q 1
q1 2
优先级队列
优先级根据数字来判断,数字为几优先级就为几,1的优先级最高
若参数中传的是元组,优先级以第一个元素的数字大小为准,如果元组的第一个元素都为字母则以字母的ASCII码为准,如果第一个元素的优先级相同就判断第二个元素的顺序(汉字也会判断顺序)但是如果同一列的元素既有数字又有字母会报错。
import queue
q2 = queue.PriorityQueue()#优先级队列
q2.put((1,2,3))
q2.put((2,2,3))
q2.put((3,2,3))
print(q2.get())
(1, 2, 3)
句柄
句柄(handle),有多种意义,第一种解释:句柄是一种特殊的智能指针。当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。
进程队列补充、socket实现服务器并发、线程完结的更多相关文章
- 利用队列Queue实现一个多并发“线程池”效果的Socket程序
本例通过利用类Queue建立了一个存放着Thread对象的“容器对象”,当Client端申请与Server端通信时,在Server端的“链接循环”中每次拿出一个Thread对象去创建“线程链接”,从而 ...
- 进程队列补充-创建进程队列的另一个类JoinableQueue
JoinableQueue同样通过multiprocessing使用. 创建队列的另外一个类: JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知 ...
- linux socket高性能服务器处理框架
这个博客很多东西 http://blog.csdn.net/luozhonghua2014/article/details/37041765 思考一种高性能的服务器处理框架 1.首先需要一个内存池 ...
- Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)
Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...
- [转帖]iis最大并发连接数、队列长度、最大并发线程数、最大工作进程数
iis最大并发连接数.队列长度.最大并发线程数.最大工作进程数 2018-10-17 12:49:03 牛兜兜 阅读数 2952 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议 ...
- Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案
本文翻译自LMAX关于Disruptor的论文,同时加上一些自己的理解和标注.Disruptor是一个高效的线程间交换数据的基础组件,它使用栅栏(barrier)+序号(Sequencing)机制协调 ...
- 线程、进程、daemon、GIL锁、线程锁、递归锁、信号量、计时器、事件、队列、多进程
# 本文代码基于Python3 什么是进程? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行 ...
- C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包
原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...
- socket编程之并发回射服务器3
在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int ...
随机推荐
- Python基本语法_变量作用域LEGB
目录 目录 软件系统 变量的作用域 高级语言对数据类型的使用过程 作用域的产生 作用域的类型 Llocal局部作用域 Eenclosing嵌套作用域 Gglobal全局作用域 Bbuilt-in内置作 ...
- golang 千位分隔符
项目需求将统计出的数字以千位分隔符分隔的形式展示(如将 1234567.89 格式化为 1,234,567.89),而 golang 的标准库中并无这样的函数. 万能的 google 找到解决办法,可 ...
- Mybatis使用时 resultMap与resultType、parameterMap与 parameterType的区别
Map:映射:Type:Java类型 resultMap 与 resultType.parameterMap 与 parameterType的区别在面试的时候被问到的几率非常高,出现的次数到了令人 ...
- Cocos2d-X网络编程(3) Cocos2d中的网络通信协议——WebSocket协议
WebSocket protocol 是HTML5一种新的协议.它实现了浏览器与服务器全双工通信.实现浏览器与服务器的即时通讯.即服务器也能主动向客户端发消息. WebSocket代理类和方法: co ...
- 【Linux开发】【Qt开发】配置tslibs触摸屏库环境设置调试对应的设备挂载点
[Linux开发][Qt开发]配置tslibs触摸屏库环境设置调试对应的设备挂载点 标签(空格分隔): [Linux开发] [Qt开发] 比如: cat /dev/input/mice cat /de ...
- sqlplus无法登陆?
关键词:error 6 initialize sqlplus,ORA-27101: shared memory realm does not exist 1.error 6 initialize sq ...
- CentOS 8 下 nginx 服务器安装及配置笔记
参考文档 nginx官方文档 安装 在CentOS下,nginx官方提供了安装包可以安装 首先先安装前置软件 sudo yum install yum-utils 然后将nginx官方源加入到yum源 ...
- 使用idea搭建SSH
一.新建项目 选中Spring strust2 hibernate 二.见项目根路径下的lib下的jar移动到WEB-INF下 移动 修改路径 在lib目录下导入[c3p0-0.9.5.2.jar]. ...
- RabbitMq学习1-介绍、安装和配置
一.简介 1.MQ框架非常之多,比较流行的有RabbitMq.ActiveMq.ZeroMq.kafka,以及阿里开源的RocketMQ 2.AMQP是消息队列的一个协议. 3.Rabbi ...
- Linux :环境变量设置和本地变量加载
bash: 全局变量: /etc/profile, /etc/profile.d/*, /etc/bashrc 个人变量: ~/.bash_profile, ~/.bashrc bash运行方 ...