并发服务器的信号传递,使服务器支持并发、GIL全局解释器锁、死锁和Rlock、信号量、event事件、...

服务器的并发实现:

服务端:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

importsocketfrom threading importThread"""服务端

1.要有固定的IP和PORT

2.24小时不间断提供服务

3.能够支持并发"""server=socket.socket()

server.bind((‘127.0.0.1‘,8080))

server.listen(5)deftalk(conn):whileTrue:try:

data= conn.recv(1024)if len(data) == 0:break

print(data.decode(‘utf-8‘))

conn.send(data.upper())exceptConnectionResetError as e:print(e)breakconn.close()"""当有客户端过来连接的时候,先执行while循环中的代码,

开启一个线程去服务这个客户端,当有客户端再过来连的时候,

可以再开启一个线程去服务一个客户端"""

whileTrue:

conn, addr= server.accept() #监听 等待客户端的连接 阻塞态

print(addr)

t= Thread(target=talk, args=(conn,))

t.start()

View Code

客户端:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

importsocket

client=socket.socket()

client.connect((‘127.0.0.1‘,8080))whileTrue:

client.send(b‘hello‘)

data= client.recv(1024)print(data.decode(‘utf-8‘))

View Code

GIL全局解释器锁:

Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。

虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。

对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全

用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)

GIL的存在是因为CPython解释器的内存管理不是线程安全的

b7a68f9ba121a6f751caa78636bac45d.png

如果同时运行,那么线程中的值可能还没有被使用就被垃圾回收机制进行回收了,所以CPython解释器的内存管理不是线程安全的,要有GIL。

垃圾回收机制

1.引用计数

2.标记清除

3.分代回收

研究python的多线程是否有用需要分情况讨论:

四个任务 计算密集型的

单核情况下

开线程更省资源

多核情况下

开进程

开线程

四个任务IO密集型的

单核情况下

开线程更节省资源

多核情况下

开线程更节省资源

代码验证:

计算密集型:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

计算密集型from multiprocessing importProcessfrom threading importThreadimportos,timedefwork():

res=0for i in range(100000000):

res*=iif __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 inl:

p.join()

stop=time.time()print(‘run time is %s‘ %(stop-start))

View Code

IO密集型:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

from multiprocessing importProcessfrom threading importThreadimportthreadingimportos,timedefwork():

time.sleep(2)if __name__ == ‘__main__‘:

l=[]print(os.cpu_count()) #本机为6核

start=time.time()for i in range(40):#p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上

p=Thread(target=work) #耗时2.051966667175293s多

l.append(p)

p.start()for p inl:

p.join()

stop=time.time()print(‘run time is %s‘ %(stop-start))

View Code

死锁和Rlock:

死锁状态:也就是不同线程都去等待对方去释放锁的情况(互相僵持)

建议:轻易不要去尝试处理锁的问题。

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

from threading importThread,Lock,current_thread,RLockimporttime"""Rlock可以被第一个抢到锁的人连续的acquire和release

每acquire一次锁身上的计数加1

每release一次锁身上的计数减1

只要锁的计数不为0 其他人都不能抢"""

#mutexA = Lock()#mutexB = Lock()

mutexA = mutexB = RLock() #A B现在是同一把锁

classMyThread(Thread):def run(self): #创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发

self.func1()

self.func2()deffunc1(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)deffunc2(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()

View Code

信号量:

过程:

先有五个线程同时获得锁,然后运行完因为不同线程睡眠时间不同所以释放时间也会不同,

之后先释放出来的锁便可以让其他线程再去抢夺。

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

#信号量可能在不同的领域中 对应不同的知识点

"""互斥锁:一个厕所(一个坑位)

信号量:公共厕所(多个坑位)"""

from threading importSemaphore,Threadimporttimeimportrandom

sm= Semaphore(5) #造了一个含有五个的坑位的公共厕所

deftask(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()

View Code

envent事件:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

from threading importEvent, Threadimporttime#先生成一个event对象

e =Event()deflight():print(‘红灯正亮着‘)

time.sleep(3)

e.set()#发信号

print(‘绿灯亮了‘)defcar(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()"""先启一个线程,打印路灯亮了,进入睡眠的同时,起了十个线程,之后打印正在等绿灯,

之后等待e.wait()收到信号,当运行e.set()发信号时,便可以继续运行了。"""

View Code

线程queue:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

importqueue"""同一个进程下的多个线程本来就是数据共享 为什么还要用队列

因为队列是管道+锁 使用队列你就不需要自己手动操作锁的问题

因为锁操作的不好极容易产生死锁现象"""

#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())

View Code

原文:https://www.cnblogs.com/yangjiaoshou/p/11353187.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值