首页 > 其他 > 详细

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

时间:2019-08-14 17:27:53      阅读:108      评论:0      收藏:0      [点我收藏+]

服务器的并发实现:

服务端:

技术分享图片
import socket
from threading import Thread

"""
服务端
    1.要有固定的IP和PORT
    2.24小时不间断提供服务
    3.能够支持并发
"""

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循环中的代码,
开启一个线程去服务这个客户端,当有客户端再过来连的时候,
可以再开启一个线程去服务一个客户端
"""
while True:
    conn, addr = server.accept()  # 监听 等待客户端的连接  阻塞态
    print(addr)
    t = Thread(target=talk, args=(conn,))
    t.start()
View Code

客户端:

技术分享图片
import socket


client = socket.socket()
client.connect((127.0.0.1,8080))

while True:
    client.send(bhello)
    data = client.recv(1024)
    print(data.decode(utf-8))
View Code

GIL全局解释器锁:

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

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

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

GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全
用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)

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

技术分享图片

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

垃圾回收机制

1.引用计数
2.标记清除
3.分代回收

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

四个任务 计算密集型
单核情况下
开线程更省资源
多核情况下
开进程 
开线程

四个任务 IO密集型
单核情况下
开线程更节省资源
多核情况下
开线程更节省资源

代码验证:

计算密集型:

技术分享图片
计算密集型
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()
    print(run time is %s %(stop-start))
View Code

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(40):
        # 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))
View Code

 死锁和Rlock:

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

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

技术分享图片
from threading import Thread,Lock,current_thread,RLock
import time
"""
Rlock可以被第一个抢到锁的人连续的acquire和release
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0 其他人都不能抢

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


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()
View Code

信号量:

过程:

先有五个线程同时获得锁,然后运行完因为不同线程睡眠时间不同所以释放时间也会不同,
之后先释放出来的锁便可以让其他线程再去抢夺。

技术分享图片
# 信号量可能在不同的领域中 对应不同的知识点
"""
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
"""
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()
View Code

envent事件:

技术分享图片
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()
"""
先启一个线程,打印路灯亮了,进入睡眠的同时,起了十个线程,之后打印正在等绿灯,
之后等待e.wait()收到信号,当运行e.set()发信号时,便可以继续运行了。
"""
View Code

线程queue:

技术分享图片
import queue
"""
同一个进程下的多个线程本来就是数据共享 为什么还要用队列

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

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

# 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

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

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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!