• python之并发编程


    一、操作系统与应用程序

      在没有介绍线程与进程相关的知识之前,需要给大家引入一个知识点,关于操作系统与应用程序又与线程与进程有什么关系?

      硬件
    ...
      装系统
      系统就是一个有程序员写出来的软件,该软件用于控制计算机硬件,让他们之间相互配合
      装软件(安装应用程序)
      ...
      并发与并行
      并发,假,由于执行速度贴别快,感觉不到停顿
      并行,真,创建10个人同时操作
      线程与进程
      单线程与单进程
      一个软件默认一个进程,可以有多个
      线程:
      工作的最小单元
      共享进程中的所有资源
      每个线程可以分担一些任务,最终完成最后的结果
      进程:
      独立开辟内存
      进程之间的数据隔离
      注:GIL锁,是在进程中起到了阻塞线程的作用
      总结:
      1.操作系统帮助开发者操作硬件
      2.程序员写好代码在操作系统上运行(依赖解释器)
      3.任务特别多
       python多线程情况下:
      计算密集型操作:效率低
      IO操作:效率高
      python多进程情况下:
      计算密集型操作:效率高(浪费资源)
      IO操作:效率高(浪费资源)
      以后写python时:
      IO密集型用多线程
      计算密集型用多进程
      java多线程情况下:
      计算密集型操作:效率高
      IO操作:效率高
      java多进程情况下:
      计算密集型操作:效率高(浪费资源)
      IO操作:效率高(浪费资源)

    GIL锁:全局解释器锁,用于限制一个进程中间同一个时刻只有一个线程被cpu调度
    扩展:默认GIL锁在执行100个指令

    二、线程/进程/协程
      线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位
     指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多进程。
      
      线程的简单示例:
        
     1 import time
     2     import threading
     3     def task():
     4         time.sleep(0)   #0秒也会有延迟
     5         print("拿快递")
     6     # 创建一个线程
     7     t = threading.Thread(target=task)
     8     # 执行线程
     9     t.start()
    10 
    11     print("干什么。。。")
    12 
    13     import time
    14     import threading
    15 
    16     def task(n):
    17         print("执行任务",n)
    18         time.sleep(5)
    19         print("...")
    20         print("任务%s 执行完毕" %n)
    21     while True:
    22         name = input("请输入任务")
    23         t = threading.Thread(target=task,args=(name,))
    24         t.start()

    所以说线程就像是流水线一样,走到哪里该作什么,是早已经规划好的。

      线程的使用:

      

    1 import threading,time
    2 def num():
    3     print("456")
    4 t = threading.Thread(target=num)
    5 t.start()
    6 print("123")
    7 # 运行结果
    8 456
    9 123

      setDaemon方法

      

     1 import threading,time
     2 def sleep(arg):
     3     time.sleep(arg)
     4     print(arg)
     5 
     6 t1 = threading.Thread(target=sleep,args=(3,))
     7 t1.start()
     8 
     9 t2 = threading.Thread(target=sleep,args=(6,))
    10 t2.setDaemon(True)
    11 t2.start()
    12 
    13 print("1")
    14 # 运行结果
    15 # 1
    16 # 3
    17 # 总结,线程中的主线程默认会等其他线程执行完毕后才关闭
    18 # 当设置setDaemon(True)的时候会不等

      join方法

      

     1 import threading,time
     2 
     3 def join(arg):
     4     time.sleep(arg)
     5     print(arg)
     6 print("123")
     7 t1 = threading.Thread(target=join,args=(3,))
     8 t1.start()
     9 t1.join()
    10 print("456")
    11 #  运行结果:
    12 # 123
    13 # 3
    14 # 456
    15 # 总结:当在子线程中添加join方法(可以加参数,表示最多等多久),则会等待子线程运行完毕后执行主线程

      获取线程的名称相关

      

     1 import threading
     2 def name():
     3     t = threading.current_thread() # 获取当前线程的对象
     4     print(t.name)
     5 
     6 t1 = threading.Thread(target=name)
     7 t1.setName("amazing")
     8 t1.start()
     9 
    10 t2 = threading.Thread(target=name)
    11 t2.setName("Magic")
    12 t2.start()
    13 # 运行结果
    14 # amazing
    15 # Magic
    16 # 总结:可以在函数中添加threading.current_thread()用来获取当前线程的对象

      线程池

     1 from concurrent.futures import ThreadPoolExecutor
     2 import time
     3 
     4 pool = ThreadPoolExecutor(5)        #创建一个线程池,并设置同时最多有5个线程可以被调度
     5 def func(arg):
     6     time.sleep(1)
     7     print(arg)
     8 for i in range(4):
     9     pool.submit(func,i)      # 去线程池中申请一个线程执行func函数
    10 # 有节制的约束线程的创建,保证线程的性能。

       进程是计算机是系统进行资源分配和调度的基本单位,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

       进程的编写

     1 import multiprocessing
     2 
     3 def task(arg):
     4     print(arg)
     5 def run():
     6     for i in range(10):
     7         p = multiprocessing.Process(target=task,args=(i,))
     8         p.start()
     9 if __name__ == '__main__':    # 在windows系统中 需要将主进程放在main方法下运行
    10     run()

      进程的使用

      join

     1 import multiprocessing,time
     2 def task(arg):
     3     time.sleep(2)
     4     print(arg)
     5 def run():
     6     for i in range(10):
     7         p = multiprocessing.Process(target=task,args=(i,))
     8         p.start()
     9         p.join(1)
    10         print("等待结束")
    11 if __name__ == '__main__':
    12     run()
    13 # 总结:join等待进程完成过后才执行

      daemon

     1 import multiprocessing,time
     2 def task(arg):
     3     time.sleep(2)
     4     print(arg)
     5 def run():
     6     print("123")
     7     p = multiprocessing.Process(target=task,args=(789,))
     8     p.daemon = True
     9     p.start()
    10 
    11     print("456")
    12 if __name__ == '__main__':
    13     run()
    14 总结:当daemon=True表示不等待

      name

     1 import multiprocessing,time
     2 def task(arg):
     3     p = multiprocessing.current_process()
     4     print(p.pid)  #p.ident
     5     time.sleep(2)
     6     print(arg)
     7 def run():
     8     for i in range(10):
     9         p = multiprocessing.Process(target=task,args=(i,))
    10         p.name = "asd"
    11         p.start()
    12         p.join(1)
    13         print("等待结束")
    14 if __name__ == '__main__':
    15     run()

      进程间的相关问题

        进程间的数据是否相互共享?

     1 import multiprocessing
     2 data_list =[]
     3 def task(arg):
     4     data_list.append(arg)
     5     print(data_list)
     6 def run():
     7     for i in range(10):
     8         p = multiprocessing.Process(target=task,args=(i,))
     9         p.start()
    10 if __name__ == '__main__':
    11     run()
    12 进程中的数据是不共享的

        怎么做才能够数据间的相互共享

          Queue()

        

     1 import multiprocessing,queue,time
     2 
     3 q = multiprocessing.Queue()
     4 def task(arg):
     5     q.put(arg)
     6 
     7 def run():
     8     for i in range(10):
     9         p = multiprocessing.Process(target=task,args=(i,))
    10         p.start()
    11     time.sleep(2)
    12     v1 = q.get()
    13     print(v1)
    14 if __name__ == '__main__':
    15     run()

          Manager()

     1 import multiprocessing,queue,time
     2 m = multiprocessing.Manager()
     3 dic = m.dict()
     4 
     5 def task(arg):
     6     dic[arg] = 100
     7 
     8 def run():
     9     for i in range(10):
    10         p = multiprocessing.Process(target=task,args=(i,))
    11         p.start()
    12         p.join()
    13     input(">>>")
    14     print(dic.valus)
    15 if __name__ == '__main__':
    16     run()

      进程池

     1 from concurrent.futures import ProcessPoolExecutor
     2 import time
     3 
     4 def task(arg):
     5     time.sleep(2)
     6     print(arg)
     7 
     8 if __name__ == '__main__':
     9     pool  = ProcessPoolExecutor(5)
    10     for i in range(10):
    11         pool.submit(task,i)

       协程不是真实存在的东西,是程序员创造出来的,协程指的是微线程。

      协程的编写

     1 import greenlet
     2 def func1():
     3     print("123")
     4     gr2.switch()
     5     print("987")
     6     gr2.switch()
     7 def func2():
     8     print("465")
     9     gr1.switch()
    10     print("789")
    11 # 协程1
    12 gr1 = greenlet.greenlet(func1)
    13 # 协程2
    14 gr2 = greenlet.greenlet(func2)
    15 
    16 # 执行协程1
    17 gr1.switch()

      

     1 基于yield实现协程
     2 def f1():
     3     print(11)
     4     yield
     5     print(22)
     6     yield
     7     print(33)
     8 
     9 def f2():
    10     print(44)
    11     yield
    12     print(55)
    13     yield
    14     print(66)
    15 v1 = f1()
    16 v2 = f2()
    17 next(v1)

      协程的作用

      对一个线程进程分片,使得线程在代码块之间进行来回切换执行,而不是按原来的流程执行。
      ...

    三、锁相关

      "锁"顾名思义,就是用来约束某种对象。

      锁的简单示例

     1 import threading,time
     2 lock = threading.RLock()  # 创建锁都对象
     3 n = 10
     4 def lk(i):
     5     print("代码不加锁",i)
     6     lock.acquire()  #加锁
     7     global n
     8     print("当前线程",i,"读取到的n值为:",n)
     9     n = i
    10     time.sleep(1)
    11     print("当前线程",i,"读取到的n值为:",n)
    12     lock.release()  #释放锁头
    13 for i in range(10):
    14     t = threading.Thread(target=lk,args=(i,))
    15     t.start()
    16 # 总结:加完锁之后此区域的代码同一时刻只能有一个线程执行
    线程安全与线程不安全的问题
    当多线程操作时,内不会让所有线程排队处理这时候线程是安全的,例如:list/dict/Queue
    线程不安全 + 人(锁) => 排队处理
    锁的用法:
      lock 普通锁 最简单的锁
     1 import threading,time
     2 v = []
     3 lock = threading.Lock()    # 创建一个lock对象
     4 def func(arg):
     5     lock.acquire()      # 添加锁
     6     v.append(arg)
     7     time.sleep(1)
     8     m = v[-1]
     9     print(arg,m)
    10     lock.release()          # 释放锁
    11 for i in range(10):
    12     t = threading.Thread(target=func,args=(i,))
    13     t.start()
    14 # 总结:在普通锁中当多个lock存在时会死锁
      Rlock 递归锁 也是最常用的锁
    import threading,time
    lock = threading.RLock()
    v = []
    def func(arg):
        lock.acquire()
        lock.acquire()      # 添加多个锁
        v.append(arg)
        time.sleep(1)
        m = v[-1]
        print(arg,m)
        lock.release()
        lock.release()
    for i in range(10):
        t = threading.Thread(target=func,args=(i,))
        t.start()
    
    # 总结:支持递归的锁多次解多次,允许多个lock同时存

      BoundedSemapore 信号锁

     1 import threading,time
     2 lock = threading.BoundedSemaphore(5)     # 表示通过设定可以放行n个线程
     3 
     4 def func(arg):
     5     lock.acquire()
     6     print(arg)
     7     time.sleep(1)
     8     lock.release()
     9 for i in range(20):
    10     t = threading.Thread(target=func,args=(i,))
    11     t.start()
    12 
    13 # 总结:可以一次放行多个线程。

      Condition 条件锁

     1 import threading,time
     2 lock = threading.Condition()
     3 def func(arg):
     4     lock.acquire()
     5     lock.wait()     # 表示等待,线程到此处停止需要一个接收值通知放行多少个线程
     6     print(arg)
     7     time.sleep(1)
     8     lock.release()
     9 for i in range(20):
    10     t = threading.Thread(target=func,args=(i,))
    11     t.start()
    12 
    13 while True:
    14     user_input = int(input("请输入要放行的个数"))
    15     lock.acquire()
    16     lock.notify(user_input)
    17     lock.release()
    18 总结: 在使用该方法时需要在主线程或其他线程中,写通知wait的方法,告诉wait放行多少个线程

      Event 事件锁

     1 import time,threading
     2 lock = threading.Event()
     3 def func(arg):
     4     lock.wait()    # 表示当线程到此处停住,相当于红灯
     5     print(arg)
     6 for i in range(10):
     7     t = threading.Thread(target=func,args=(i,))
     8     t.start()
     9 user_input = input("输入红灯或绿灯")
    10 if user_input =="绿灯":
    11     lock.set()    # 表示一次性释放所有线程 ,相当于绿灯
    12 else:
    13     pass
    14 
    15 # 总结: 一次性放行所有

       进程锁的使用方法大体与线程数相同,只是在导入模块时是import multiprocessing,调用的时候也是。

    
    
    为甚么要用锁?
    1.线程安全,列表和字典线程安全:
    非线程安全
    控制一段代码
      2.如果多个线程同时修改一个值的时候需要加锁
    线程锁/进程锁/GIL锁
      线程锁是为了在线程进行数据操作时保证数据的安全性(涉及到线程安全问题)
      进程锁是为了在数据共享时起到防护的作用
      GIL锁是为了限制同一个进程中只能有一个线程进入python解释器

    threading.local()  *
     1 import threading,time
     2 v = threading.local()
     3 
     4 def func(arg):
     5     v.space = arg   # 在函数内部会创建一个属于自己的空间
     6     time.sleep(1)
     7     print(v.space,arg)
     8 
     9 for i in range(10):
    10     t = threading.Thread(target=func,args=(i,))
    11     t.start()
    12 
    13 # 总结:为不同的线程创建一个空间(字典),可以去当前自己空间里面存取值
    
    
    生产者消费者模型
    生产者
      队列:先进先出
      栈:先进后出
    消费者
    更便利

      简单示例:
     1 import queue,threading,time
     2 
     3 q = queue.Queue()
     4 
     5 def producer(id):
     6     """
     7     表示生产者
     8     :param id:
     9     :return:
    10     """
    11     while True:
    12         time.sleep(1)
    13         q.put("发送信息")
    14         print("%s号用户发送信息"%id)
    15 
    16 for i in range(1,5):
    17     t =threading.Thread(target=producer,args=(i,))
    18     t.start()
    19 
    20 def consumer(id):
    21     """
    22     表示消费者
    23     :param id:
    24     :return:
    25     """
    26     while True:
    27         time.sleep(1)
    28         q.get("获得信息")
    29         print("%s号客服获得信息"%id)
    30 
    31 for i in range(1,5):
    32     t =threading.Thread(target=consumer,args=(i,))
    33     t.start()
    34 
    35 # 总结:生产者消费者模型解决了不用一直等待的问题
     
    
    




    。。。

      
  • 相关阅读:
    Thymeleaf中,将字符串作为js函数的参数
    测试开发面试题总结
    013_RomanToInteger
    Python列表中查找某个元素的索引(多个)
    Python“函数式编程”中常用的函数
    009_Palindrome Number
    Python字符串方法总结(一)
    007_Reverse Integer
    002_Add Two Numbers
    pycharm上传代码到github
  • 原文地址:https://www.cnblogs.com/qq631243523/p/9618708.html
Copyright © 2020-2023  润新知