zoukankan      html  css  js  c++  java
  • 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多秒(因为线程只能并发)

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

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

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

    #计算密集型
    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密集型
    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())
  • 相关阅读:
    for循环,pydev提示未使用的变量,解决办法
    sc 与net命令的区别
    Selenium测试Ajax程序(转)
    Python yield 使用浅析(转)
    python操作Excel读写--使用xlrd
    Python操作Mysql实例代码教程在线版(查询手册)
    MySQL之alter语句用法总结
    使用WebDriver遇到的那些坑(转)
    python 列表转为字典的两个小方法
    python
  • 原文地址:https://www.cnblogs.com/wangnanfei/p/11353476.html
Copyright © 2011-2022 走看看