TCP 服务端实现并发:
服务端通工作原理通俗讲就是一个人站门口一直等,一有客户来就领进去,但是正常情况只能接一个人,
进去的人被服务完毕才能领下一个进来,而实现并发呢就是让多个客户端感觉是在同时运行,让多个客户感觉同时在被服务。
方法可以改为门口站一个人 来一个人往里面吼一声 让里面的人把它接走,来一个里面人接一个,来一个里面人接一个
让多个客户感觉像是同时在被服务,从而实现并发的效果
里面的人可以是进程,也可以是线程,
进程消耗资源比较大,所以一般选择开启多线程
只要开多线程就能解决这个问题。
代码实现:
# 服务端.py代码演示:
import socket
from threading import Thread
server = socket.socket()
server.bind(("127.0.0.1", 8080))
server.listen(5)
def talk(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break
print(data.decode("utf-8"))
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close()
while True:
conn, addr = server.accept() # 等待客户端的连接
print(addr)
t = Thread(target=talk, args=(conn,))
t.start()
# 客户端.py代码演示:
import socket
client = socket.socket()
client.connect(("127.0.0.1", 8080))
while True:
client.send(b"sweet")
data = client.recv(1024)
print(data.decode("utf-8"))
GIL全局解释器锁:
python代码想要被运行起来,首先要有一个python解释器
python 解释器有很多种,我们常见的是Cpython
GIL本质也是一把互斥锁,将并发变成串行,保证数据的安全,牺牲了效率
GIL阻止同一个进程下多个线程的同时执行,同一个进程下多个线程无法实现并行,但可以实现并发。
GIL无法利用多核优势每次运行每次只能用一个cpu,cpu不确定,但是GIL这把锁要必须用。
GIL之所以存在是因为cpython解释器的内存管理不管线程的安全,线程是执行单位,但是不是立马执行的,需要python解释器帮忙解释成计算机语言才行。
GIL 是单程下多个线程无法利用多核优势,是所有解释型语言的通病 因为解释型语言是读一行,走一行,计算机根本不知道你下一行要干嘛,要进行什么操作
没办法帮你提前规划后面的路线,所以GIL在解释型预言中只能保证同一时刻只有一个线程对共享志愿进行存取。
# 验证GIL以及锁的特点
import time
from threading import Thread
n = 100
def task():
global n
tmp = n
time.sleep(1)
n = tmp - 1
t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(n)
验证python多线程是否有用:
# 计算密集型
from multiprocessing import Process
from threading import Thread
import os,time
def work():
res=0
for i in range(100000000):
res*=i
if __name__ == '__main__':
l=[]
print(os.cpu_count()) # 本机为6核
start=time.time()
for i in range(6):
# p=Process(target=work) # 耗时 4.732933044433594
p=Thread(target=work) # 耗时 22.83087730407715
l.append(p)
p.start()
for p in l:
p.join()
stop=time.time()
# IO密集型
from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
time.sleep(2)
if __name__ == '__main__':
l=[]
print(os.cpu_count()) #本机为6核
start=time.time()
for i in range(400):
p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上
# p=Thread(target=work) #耗时2.051966667175293s多
l.append(p)
p.start()
for p in l:
p.join()
stop=time.time()
print('run time is %s' %(stop-start))
print('run time is %s' %(stop-start))
"""
研究python的多线程是否有用需要分情况讨论
四个任务 计算密集型的 10s
单核情况下
开线程更省资源
多核情况下
开进程 10s
开线程 40s
四个任务 IO密集型的
单核情况下
开线程更节省资源
多核情况下
开线程更节省资源
python的多线程到底有没有用
需要看情况而定 并且肯定是有用的
一般推荐多进程+多线程配合使用
"""
死锁现象:
就算能够熟练运用acquire和release也不能保证自己不会被自己锁上,就是在处理两个锁的时候很容易出现死锁现象。
比如说现在偶锁A和锁B两个锁,然后几个人一起抢锁A和锁B你第一个抢到了锁A,后面的人必须等着你释放锁A才能再去抢锁A, 然后抢到锁A的你这个时候又去抢锁B,你在抢锁B的时候肯定没人抢,因为其他人还在等待你释放锁A。然后你释锁B没人抢,再释放锁A, 去抢锁B,这个时候别人都在抢锁A,所以没人跟你抢锁B,但是现在你有锁B的情况下想要再去抢锁A的时候发现没法抢了,因为想要抢锁A就得等别人把锁A给释放掉,但是别人想要释放锁A就得等你释放锁B!这时候就出现了死锁现象。
只要类加括号实例化对象,无论传入的参数是否一样生成的对象肯定不一样。(单利模式除外)
from threading import Thread,Lock,current_thread
import time
mutexA = Lock()
mutexB = Lock()
class MyThread(Thread):
def run(self): # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print('%s抢到了A锁'%self.name) # self.name等价于current_thread().name
mutexB.acquire()
print('%s抢到了B锁'%self.name)
mutexB.release()
print('%s释放了B锁'%self.name)
mutexA.release()
print('%s释放了A锁'%self.name)
def func2(self):
mutexB.acquire()
print('%s抢到了B锁'%self.name)
time.sleep(1)
mutexA.acquire()
print('%s抢到了A锁' % self.name)
mutexA.release()
print('%s释放了A锁' % self.name)
mutexB.release()
print('%s释放了B锁' % self.name)
for i in range(10):
t = MyThread()
t.start()
class Demo(object):
pass
obj1 = Demo()
obj2 = Demo()
print(id(obj1),id(obj2))
递归锁(Rlock):
可以连续的被acquire,但是只有第一抢到锁的人才能连续的acquire和release,
没acquire一次锁身上的计数加一,每release一次锁身上的计数减一。
而且只要锁身上的计数不为零,其他人都不能抢。
from threading import Thread,Lock,current_thread,RLock
import time
mutexA = mutexB = RLock()
class MyThread(Thread):
def run(self): # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print('%s抢到了A锁'%self.name) # self.name等价于current_thread().name
mutexB.acquire()
print('%s抢到了B锁'%self.name)
mutexB.release()
print('%s释放了B锁'%self.name)
mutexA.release()
print('%s释放了A锁'%self.name)
def func2(self):
mutexB.acquire()
print('%s抢到了B锁'%self.name)
time.sleep(1)
mutexA.acquire()
print('%s抢到了A锁' % self.name)
mutexA.release()
print('%s释放了A锁' % self.name)
mutexB.release()
print('%s释放了B锁' % self.name)
for i in range(10):
t = MyThread()
t.start()
class Demo(object):
pass
obj1 = Demo()
obj2 = Demo()
print(id(obj1),id(obj2))
信号量(Thread):
# 信号量可能在不同的领域中 对应不同的知识点
"""
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
"""
from threading import Semaphore,Thread
import time
import random
sm = Semaphore(5) # 造了一个含有五个的坑位的公共厕所
def task(name):
sm.acquire()
print('%s占了一个坑位'%name)
time.sleep(random.randint(1,3))
sm.release()
for i in range(40):
t = Thread(target=task,args=(i,))
t.start()
event事件:
子线程等待另一个子线程进程结束。就是一个子进程给另一个子进程发信号,另一个子进程收不到这个信号永远不走。
from threading import Event,Thread
import time
# 先生成一个event对象
e = Event()
def light():
print('红灯正亮着')
time.sleep(3)
e.set() # 发信号
print('绿灯亮了')
def car(name):
print('%s正在等红灯'%name)
e.wait() # 等待信号
print('%s加油门飙车了'%name)
t = Thread(target=light)
t.start()
for i in range(10):
t = Thread(target=car,args=('伞兵%s'%i,))
t.start()
线程q:
# 第一种正常存取
q = queue.Queue()
q.put('hahha')
print(q.get())
# 第二种堆栈 先进后出,最后进的最先出来
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
# 第三种 支持负数,按优先级取,数字越小优先级越高
q = queue.PriorityQueue()
# 数字越小 优先级越高
q.put((10,'haha'))
q.put((100,'hehehe'))
q.put((0,'xxxx'))
q.put((-10,'yyyy'))
print(q.get())