TCP并发、GIL、锁

TCP实现并发

#client客户端
import socket

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

while True:
    msg = input('>>>:').strip()
    if len(msg) == 0:continue
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data.decode('utf-8'))


#server服务端
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()

#通过创建线程的方式,让线程来“接待”连入的客户端从而达到并发的效果

 

GIL全局锁解释器

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

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

CIL的存在是因为Cpython解释器的内存管理,而不是线程安全。

 

 

#用代码来验证
from threading import Thread
import time

n = 100

def task():
    global n
    tmp = n
    #time.sleep(1) # 如果让程序睡眠一秒(即出现I/O操作,会自动释放锁,导致最后的到的结果是99,这是因为在你释放锁了之后别的子线程也可以抢锁拿到这个数据,然后再进行操作,如果不让程序睡眠(不让程序出现I/O操作的话),那么就和普通的互斥锁一样,谁抢到谁来运行,最后结果是0)
    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多线程是否有用的讨论

因为python的多线程并不能利用多核优势,那么python的多线程是否还有用?

这个需要分情况讨论:

在计算密集型的任务时(比如有4个任务,每个任务是10秒)

单核情况下:

开线程更省资源(因为单核情况下都是并发,开线程的资源明显比开进程的资源少)

多核情况下:

 

开进程可能是10秒多(多核的情况下,开进程可以达到一个并行的效果,所以4个一起执行的时间相当于每一个的时间),而开线程可能需要40多秒(因为线程只能并发)

#计算密集型
from multiprocessing import Process
from threading import Thread
import os,time
def work():
    res=0
    for i in range(1000):
        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))
#多核的情况下,计算密集型开进程比开线程快

 

在I/O密集型的任务时(同样是4个任务)

单核情况下和多核情况下都是开线程更节省资源

因为I/O型的任务需要不停的切换,所以即使是多个进程也需要在阻塞态等待,而开线程明显更节省资源。

#I/O密集型
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(4000):
        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))
#I/O密集型的情况下开线程比开进程快

 

 

 

死锁与递归锁

#死锁
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()
#当出现Thread-1抢到了锁B,Thread-2抢到了锁A的时候,程序卡主,也就是进入了死锁状态,
这是因为Thread-1在执行到func2中的抢锁B时,锁A被Thread-2抢到了,这时Thread-1的下一步是抢锁A,
但被Thread-2已经占住了,而Thread-2下一步要抢的锁B此时被Thread-1占住,谁都进行不了下一步,所以卡住,
于是出现了死锁现象。
#递归锁
from threading import Thread,RLock,current_thread
import time

mutexA = mutexB = RLock()

class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print('%s抢到了锁A'%self.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()
#递归锁的意思就是这个锁可以被同一个人多次抢,也就是说当第一个人抢到这个把锁时,
锁的计数会加一,此时别人无法抢这把锁,但这个人还可以再抢这把锁,然后计数继续加一,
只要锁上有计数的时候别人就不能抢这把锁,但释放锁的时候,计数有多少就要释放多少次,
释放完之后别人才可以抢。

 

 

信号量

# 信号量可能在不同的领域中 对应不同的知识点
"""
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
"""
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()  #设置event的状态为True,唤醒被阻塞的线程# 发信号
    print('绿灯亮了')

def car(name):
    print('%s正在等红灯'%name)
    e.wait()  # 设置event的状态为False,阻塞线程# 等待信号
    print('%s加油门飙车了'%name)

t = Thread(target=light)
t.start()

for i in range(10):
    t = Thread(target=car,args=('伞兵%s'%i,))
    t.start()
#event的作用是,生产一个Event对象,在线程执行到某个阶段的时候我们可以设置一个Event对象,
通过改变event对象的状态来阻塞或者唤醒线程的执行。

 

 

线程q(队列)

同一个进程下的多个线程本来就是数据共享,为什么还要用队列,因为队列是管道+锁,使用队列就不需要你来自己手动操作锁的问题,而且锁操作的不好容易产生死锁现象。

import queue
q = queue.Queue()  # 先进先出
q.put('hahaha')
q.put('hihihi')
print(q.get())

q = queue.LifoQueue()  #后进先出
q.put(1)
q.put(2)
q.put(3)
print(q.get())

q = queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小(可以是负的)优先级越高
q.put((10,'haha'))
q.put((100,'hehehe'))
q.put((0,'xxx'))
q.put((-10,'yyy'))
print(q.get())

 

posted on 2019-08-14 18:51  AbrahamChen  阅读(177)  评论(0编辑  收藏  举报

导航