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实现服务器并发、线程完结的更多相关文章

  1. 利用队列Queue实现一个多并发“线程池”效果的Socket程序

    本例通过利用类Queue建立了一个存放着Thread对象的“容器对象”,当Client端申请与Server端通信时,在Server端的“链接循环”中每次拿出一个Thread对象去创建“线程链接”,从而 ...

  2. 进程队列补充-创建进程队列的另一个类JoinableQueue

    JoinableQueue同样通过multiprocessing使用. 创建队列的另外一个类: JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知 ...

  3. linux socket高性能服务器处理框架

    这个博客很多东西 http://blog.csdn.net/luozhonghua2014/article/details/37041765   思考一种高性能的服务器处理框架 1.首先需要一个内存池 ...

  4. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  5. [转帖]iis最大并发连接数、队列长度、最大并发线程数、最大工作进程数

    iis最大并发连接数.队列长度.最大并发线程数.最大工作进程数 2018-10-17 12:49:03 牛兜兜 阅读数 2952   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议 ...

  6. Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案

    本文翻译自LMAX关于Disruptor的论文,同时加上一些自己的理解和标注.Disruptor是一个高效的线程间交换数据的基础组件,它使用栅栏(barrier)+序号(Sequencing)机制协调 ...

  7. 线程、进程、daemon、GIL锁、线程锁、递归锁、信号量、计时器、事件、队列、多进程

    # 本文代码基于Python3 什么是进程? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行 ...

  8. C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包

    原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...

  9. socket编程之并发回射服务器3

    在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int ...

随机推荐

  1. Linux_Samba详解

    目录 目录 Samba Server Parameter Configuration file explain Setup the Samba Server Access the samba shar ...

  2. 阶段3 1.Mybatis_09.Mybatis的多表操作_8 mybatis多对多操作-查询角色获取角色下所属用户信息

    一个角色对应多个用户 生成getter和setter 查看两个表的数据 中间表定义了谁有角色,谁没有角色 根据中间表的关系,最终查询出来的列表的数据样子.这需要两个左外链接才能实现功能. 第一个左外链 ...

  3. MySQL单列索引和组合索引的创建及区别介绍

    MySQL单列索引是我们使用MySQL数据库中经常会见到的,MySQL单列索引和组合索引的区别可能有很多人还不是十分的了解,下面就为您分析两者的主要区别,供您参考学习. 为了形象地对比两者,再建一个表 ...

  4. Servlet 三种创建方式

    servlet 是运行在 Web 服务器(tomcat)中的小型 Java 程序(即:服务器端的小应用程序) (其实就是一个java类,只不过不用再new了).servlet 通常通过 HTTP(超文 ...

  5. cocos2dx基础篇(19) 基本动作CCAction

    [3.x]     (1)去掉"CC"     (2)新增了一些动作:(精力有限,新增的动作请自行摸索) > EaseBezierAction > EaseQuadra ...

  6. [Web 前端] 026 jQuery 初探

    目录 1. jQuery 简介 2. jQuery 的简单操作 2.1 jQuery 选择器 2.1.1 简介 2.1.2 基础选择器 2.2 过滤获取 2.3 父子关系获取 3. jQuery 元素 ...

  7. 数组转字符串 java8

    public static String arrayToString(int[] arr) { // 1,2,3... StringBuffer sb = new StringBuffer(); fo ...

  8. [2019沈阳网络赛D题]Dawn-K's water(点分治)

    题目链接 题意为求出树上任意点对的距离对3取余的和. 比赛上听到题意就知道是点分治了,但是越写越不对劲,交之前就觉得会T,果不其然T了.修修改改结果队友写了发dp直接就过了Orz. 赛后想了想维护的东 ...

  9. arm初识

    一.CPU从指令集角度分类 1.1. CISC  (X86) 1.1. 1. CISC CPU 指complex instruction set computer复杂指令集CPU 1.1.2. CIS ...

  10. HDU4471 Homework

    题目 预处理转移矩阵的\(2^k\). 然后把关键点按下标排序. 每次用类似于矩阵快速幂的方法求出两个关键点中间的转移矩阵. #include<bits/stdc++.h> using n ...