• 进程、线程与GIL全局解释器锁详解


     (1)python下多线程的限制以及多进程中传递参数的方式
      python多线程有个全局解释器锁(global interpreter lock),这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不是“并行”。
      多进程间共享数据,可以使用 multiprocessing.Value 和 multiprocessing.Array

      (2)python多线程与多进程的区别  

      • 在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。

    多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。

    进程与线程的关系:

    1.   线程是最小的调度单位
    2.  进程是最小的管理单元
    3.  一个进程必须至少一个线程
    4.  没有线程,进程也就不复存在

    线程特点:

    3     线程的并发是利用cpu上下文的切换(是并发,不是并行4     多线程执行的顺序是无序的
    5     多线程共享全局变量
    6     线程是继承在进程里的,没有进程就没有线程
    7     GIL全局解释器锁
    8     只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就    
    9        很合适

    线程详解:

    import threading  # ---》导入模块
        # def func(n):
        #     print('task',n)
        # t = threading.Thread(target = func,args = (1,))
        # t.start()
        #
        #
        # for i in range(10):        ---》使t的内容循坏输出10行
        #     t = threading.Thread(target = func,args = ('t-%s'%i,))  ---》target=函数名 args要求以元组形式传参,当参数只有一个时,以(参数,)的格式传参。
        #     t.start()       ----》固定启动线程
    
        # 多线程共享全局变量
    
        # g=0    ---》设置一个全局变量
        # def test():
        #     global g              ----》线程共享全局变量时需要建立在声明global+全局变量上
        #     for i in range(10):
        #         g +=1
        #     print(g)
        # def test1():
        #     global g
        #     for i in range(10):
        #         g +=1
        #     print(g)
        # t1=threading.Thread(target=test)     ---->用线程调用test函数的结果,没有参数时,只需要输入target就行
        # t2=threading.Thread(target=test1)
        # t1.start()
        # t2.start()

     协程:

    资源的自动切换,耗资源小,效率高。

    GIL全局解释器锁:

    #GIL全局解释器锁        ---作用是保证最多只有一个线程在使用全局变量g_num,但是不保证赋值成功
    lock = threading.Lock()   ----添加互斥锁,作用是使两个线程不再并发处理,使赋值成功,注意L要大写
    g_num = 0
    def test1():
        global g_num
        lock.acquire()        ---》在循坏前锁上
        for i in range(100000):
            g_num +=1
        lock.release()      ----》赋值结束后解放锁
    def test2():
        global g_num
        lock.acquire()
        for i in range(100000):
            g_num +=1
        lock.release()
    t1=threading.Thread(target=test1)       ---》注意T要大写
    t2=threading.Thread(target=test2)
    t1.start()      #  导致结果不对的原因(1):主线程处理py文件速度比另外两个线程赋值速度快,在没赋值结束时就又开始新一轮的赋值
    t2.start()
    
    # 解决方法(1):
    # 在后面加上两行
    t1.join()   #----》即主线程等待每轮赋值结束后才重启下一轮赋值,但是这样也还是不对,只能让成功次数上升
    t2.join()   # 因为赋值成功率不是100%,次数越多越有可能赋值失败。
    print(g_num)
    
    解决方法(2):
    添加互斥锁:lock = threading.Lock() ,保证赋值成功,缺点是速度慢

    进程详解:

    # 一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
    # 进程之间是相互独立的,结果互不影响也无法共享全局变量,等待上一个进程结束,下一个进程再执行。
    # cpu密集的时候适合用多进程
    #cpu效率高,耗资源多,
    
    import multiprocessing    --》导入进程模块
    import time  ----》导入快捷键,将鼠标停在关键词上alt 加 enter 选择导入模块
    
    
    def test1(n):
        time.sleep(2)       ---》两秒后打印执行任务完毕,再到第二个进程
        print('task',n)
    
    def test2(n):
        time.sleep(2)
        print('task',n)
    if __name__ == '__main__':    --->处理py文件,这是主进程的作用
        p1 = multiprocessing.Process(target=test1,args=(1,))     --》注意P的大写
        p2 = multiprocessing.Process(target=test2,args=(1,))
        p1.start()      ---》此进程负责调用p1进程
        p2.start()     ---》负责调用p2进程

    进程池:

    import multiprocessing
    import time                   #进程池的并发必须导入和引用时间模块
    g_num = 0
    def test1(n):
        for i in range(n):
            time.sleep(1)
            print('test1', i)
    
    def test2(n):
        for i in range(n):
            time.sleep(1)
            print('test2', i)
    def test3(n):
        for i in range(n):
            time.sleep(1)
            print('test3', i)
    
    def test4(n):
        for i in range(n):
            time.sleep(1)
            print('test4', i)
    
    if __name__ == '__main__':                  #此操作必须存在,不然无法调用
        pool = multiprocessing.Pool(3)          #把进程声明出来括号里不写东西说明无限制,如果写数字,就是最大的进程数,即允许几个进程并发。池外的串行
        pool.apply_async(test1,(5,))           #用pool去调用函数test1,参数为5格式为(5,)
        pool.apply_async(test2,(5,))
        pool.apply_async(test3,(5,))
        pool.apply_async(test4,(5,))          #以上三个进程并发处理,同时输出赋值,而4进程串行输出,因为在进程池外**
        pool.close()
       pool.join() #close必须在join的前面
  • 相关阅读:
    MySQL索引
    MySQL事物
    《软件设计师》——计算机网络
    《软件设计师》考点分布
    《软件设计师》——UML
    《软件设计师》——法律法规与标准化知识
    《软件设计师》——多媒体基础
    《软件设计师》——信息安全基础
    《软件设计师》——数据库系统
    《软件设计师》——数据结构和算法基础
  • 原文地址:https://www.cnblogs.com/zzzynx/p/10774817.html
Copyright © 2020-2023  润新知