zoukankan      html  css  js  c++  java
  • 昨日内容回顾
    	IPC机制
    		进程间通信
    			进程与进程之间是数据隔离的
    			管道/队列(管道+锁)
    				队列:先进先出
    				堆栈:先进后出
    				q.put()  放入值
    				q.get()  获取队列里面的值(同一时刻只能有一个任务来队列中获取数据)
    				两者在存放值和取值的时候都会出现阻塞的情况(队列满了,队列空了)
    		
    		
    		生产者消费者模型
    			生产者:生产数据(做包子的)
    			消费者:处理数据(吃包子的)
    			两者之间的通信介质:队列/管道
    			供需不平衡:
    				队列:	生产者生产的数据放到队列里面
    						消费者去队列里面获取数据
    		
    		线程理论
    		
    			把操作系统比喻成工厂
    			进程:资源单位(工厂里面的车间)
    			线程:执行单位(车间里面的流水线)
    			任何一个进程都自带一个"主"线程
    			
    			进程中的线程数据是共享的,
    			开起进程的开销要远远大于开启线程的开销
    			开启进程:申请空间,拷贝代码都需要耗时
    			开启线程:开销极小,几乎在代码执行的同时线程就已经创建
    		
    				
    		如何起线程?(跟起进程一样一样的)
    			两种起线程的方式:
    				1.通过制定target起
    				2.通过继承类的方式起
    		
    		线程join
    			主线程等待子线程运行完毕
    		
    		线程之间数据共享
    			多个线程操作同一份数据???出现数据不安全的情况
    			设计到多个线程或进程操作同一份数据的时候,通常都需要将并行并发变成串行
    			虽然牺牲了效率但是提高了数据的安全性
    			针对不同的数据,需要加不同的锁
    			锁(独立卫生间)
    		
    		其他属性和方法
    			current_thread().name
    			active_count  当前活跃的线程数
    			
        
    		守护线程
    			主线程必须等待所有非守护线程的结束才能结束
    

    1.全局解释器锁GIL

    '''
    定义:
    In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple 
    native threads from executing Python bytecodes at once. This lock is necessary mainly 
    because CPython’s memory management is not thread-safe. (However, since the GIL 
    exists, other features have grown to depend on the guarantees that it enforces.)
    '''
    结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
    
    • GIL其实就是一把互斥锁(牺牲了效率但是保证了数据的安全)。

    • 线程是执行单位,但是不能直接运行,需要先拿到python解释器解释之后才能被cpu执行

    • 同一时刻同一个进程内多个线程无法实现并行,但是可以实现并发

    • 为什么要有GIL是因为它内部的垃圾回收机制不是线程安全的

    • 垃圾回收机制也是一个任务,跟你的代码不是串行运行,如果是串行会明显有卡顿

    • 这个垃圾回收到底是开进程还是开线程?肯定是线程,线程肯定也是一段代码,所以想运行也必须要拿到python解释器

    • 假设能够并行,会出现什么情况?一个线程刚好要造一个a=1的绑定关系之前,这个垃圾线程来扫描,矛盾点就来了,谁成功都不对!

    • 也就意味着在Cpython解释器上有一把GIL全局解释器锁

    • 同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行

    1.python中的多线程到底有没有用?

    单核情况下:四个任务

    多核情况下:四个任务

    计算密集型:一个任务算十秒,四个进程和四个线程,肯定是进程快

    IO密集型:任务都是纯io情况下,线程开销比进程小,肯定是线程好

    
    垃圾回收机制
        1.引用计数
        2.标记清除
        3.分代回收
        
    同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行
    
    问题:python多线程是不是就没有用了呢?
    四个任务:计算密集的任务   每个任务耗时10s
    单核情况下:
        多线程好一点,消耗的资源少一点
    多核情况下:
        开四个进程:10s多一点
        开四个线程:40s多一点
    
    四个任务:IO密集的任务   每个任务io 10s
    单核情况下:
        多线程好一点
    多核情况下:
        多线程好一点 
    多线程和多进程都有自己的优点,要根据项目需求合理选择
    
    # 计算密集型
    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())  # 本机为12核
        start=time.time()
        for i in range(12):
            # p=Process(target=work) #耗时8s多
            p=Thread(target=work) #耗时44s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))
    
    # 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()) #本机为12核
        start=time.time()
        for i in range(400):
            p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上
            # p=Thread(target=work) #耗时2s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))
    
    
    #### 2.GIL与自定义互斥锁
    
    不同的数据需要加不同的锁才能保证数据的安全,GIL锁只是对线程加锁,对数据并没有加锁的效果
    
    ​```python
    from threading import Thread,Lock
    import time
    
    mutex=Lock()
    n=100
    def task():
        global n
        with mutex:
            temp=n
            time.sleep(0.1)
            n=temp-1
    
    if __name__ == '__main__':
        l=[]
        for i in range(100):
            t=Thread(target=task)
            l.append(t)
            t.start()
    
        for t in l:
            t.join()
        print(n)
    # 对于修改不同的数据,需要加不同的锁进行处理
    

    3.死锁与递归锁(了解)

    自定义锁一次acquire必须对应一次release,不能连续acquire

    递归锁可以连续的acquire,每acquire一次计数加一

    递归锁可以连续的acquire,每acquire一次计数加一:针对的是第一个抢到我的人
    
    from threading import Thread,Lock,RLock
    import time
    
    # mutexA=Lock()
    # mutexB=Lock()
    mutexB=mutexA=RLock()  # 抢锁之后会有一个计数 抢一次计数加一 针对的是第一个抢到我的人
    
    
    class Mythead(Thread):
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            mutexA.acquire()
            print('%s 抢到A锁' %self.name)
            mutexB.acquire()
            print('%s 抢到B锁' %self.name)
            mutexB.release()
            mutexA.release()
    
        def f2(self):
            mutexB.acquire()
            print('%s 抢到了B锁' %self.name)
            time.sleep(2)
            mutexA.acquire()
            print('%s 抢到了A锁' %self.name)
            mutexA.release()
            mutexB.release()
    
    if __name__ == '__main__':
        for i in range(100):
            t=Mythead()
            t.start()
    

    4.信号量(了解)

    自定义的互斥锁如果是一个厕所,那么信号量就相当于公共厕所,门口挂着多个厕所的钥匙。抢和释放跟互斥锁一致

    from threading import Thread,Semaphore
    import time
    import random
    sm = Semaphore(5)  # 公共厕所里面有五个坑位,在厕所外面放了五把钥匙
    
    def task(name):
        sm.acquire()
        print('%s正在蹲坑'%name)
        # 模拟蹲坑耗时
        time.sleep(random.randint(1,5))
        sm.release()
    
    
    if __name__ == '__main__':
        for i in range(20):
            t = Thread(target=task,args=('伞兵%s号'%i,))
            t.start()
    

    5.Event事件

    一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样

    from threading import Thread,Event
    import time
    event = Event()  # 造了一个红绿灯
    
    
    def light():
        print('红灯亮着的')
        time.sleep(3)
        print('绿灯亮了')
        event.set()
    
    
    def car(name):
        print('%s 车正在等红灯'%name)
        event.wait()
        print('%s 车加油门飙车走了'%name)
    
    
    if __name__ == '__main__':
        t = Thread(target=light)
        t.start()
    
        for i in range(10):
            t = Thread(target=car,args=('%s'%i,))
            t.start()
    

    6.线程queue

    同一个进程下的线程数据都是共享的为什么还要用queue?queue本身自带锁的功能,能够保证数据的安全

    # 我们现在的q只能在本地使用,后面我们会学基于网络的q 
    import queue
    
    queue.Queue() #先进先出
    q=queue.Queue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())
    
    queue.LifoQueue() #后进先出->堆栈
    q=queue.LifoQueue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())
    
    queue.PriorityQueue() #优先级
    q=queue.PriorityQueue(3) #优先级,优先级用数字表示,数字越小优先级越高
    q.put((10,'a'))
    q.put((-1,'b'))
    q.put((100,'c'))
    print(q.get())
    print(q.get())
    print(q.get())
    
  • 相关阅读:
    设计模式——装饰模式(Decorator Pattern)
    设计模式——策略模式(Strategy Pattern)
    设计模式——简单工厂模式(SimpleFactory Pattern)
    入博客园三周年记
    android+Service
    surfaceView+canvas+paint+bitmap
    Enable Sublime text 2 to support GBK in Mac
    androidstudio+opencv
    mac下的环境变量PATH
    Curl命令
  • 原文地址:https://www.cnblogs.com/huangxuanya/p/10851150.html
Copyright © 2011-2022 走看看