• 线程&进程


    扯淡的yuan

    并发&并行

      并发:是指系统具有处理多个任务(动作)的能力。

      并行:是指系统具有 同时 处理多个任务(动作)的能力。

    同步&异步

      同步:当进程执行到一个IO(等待外部数据)的时候,-----等:同步

      异步:                      -----不等:一直等到数据接受成功,再回来处理

    问题: 多核没利用上?

      GIL:全局解释锁

        因为有GIL,所以,同一时刻,只有一个线程被CPU执行

    任务:IO密集型 & 计算密集型

      对于IO密集型的任务  :python的多线程的是有意义的

                   可以采用多进程+协程

      对于计算密集型的任务:python的多线程就不推荐,python就不适用了

    线程与进程的区别:

    1 一个程序至少有一个进程,一个进程至少有一个线程.(进程可以理解成线程的容器)
    
    2 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    
    3 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和
      程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 
    
    4 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调
      度的一个独立单位. 
      线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程
      自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是
      它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 
      一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
    无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行
    python的GIL

    线程

    import threading
    import time
     
    def sayhi(num): #定义每个线程要运行的函数
     
        print("running on number:%s" %num)
     
        time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
        t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
     
        t1.start() #启动线程
        t2.start() #启动另一个线程
     
        print(t1.getName()) #获取线程名
        print(t2.getName())
    直接调用
    import threading
    import time
    
    
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
    
        def run(self):#定义每个线程要运行的函数
    
            print("running on number:%s" %self.num)
    
            time.sleep(3)
    
    if __name__ == '__main__':
    
        t1 = MyThread(1)
        t2 = MyThread(2)
        t1.start()
        t2.start()
        
        print("ending......")
    继承调用

     其他方法:

    # run():  线程被cpu调度后自动执行线程对象的run方法
    # start():启动线程活动。
    # isAlive(): 返回线程是否活动的。
    # getName(): 返回线程名。
    # setName(): 设置线程名。
    
    threading模块提供的一些方法:
    # threading.currentThread(): 返回当前的线程变量。
    # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    例:

    import threading
    from time import ctime,sleep
    import time
    def music(func):
            print("Begin listening to %s. %s"%(func,ctime()))
            sleep(4)
            print("end listening %s"%ctime())
    def move(func):
            print("Begin watching at the %s!%s"%(func,ctime()))
            sleep(5)
            print("end watching %s"%ctime())
    threads=[]#用来放线程的对象
    t1=threading.Thread(target=music,args=("七里香",))
    threads.append(t1)
    t2=threading.Thread(target=move,args=("啊甘正传",))
    threads.append(t2)
    if __name__ == '__main__':
        t1.setDaemon(True)#守护线程,主进程关闭则t1关闭。
        t2.setDaemon(True)#主线程关闭时则关闭
        for t in threads:#循环得到线程的实例
            t.start()#开启线程
            # print(threading.currentThread())#打印线程的对象
            # print(threading.enumerate())#打印正在开启的线程列表
            # print(threading.activeCount())#打印正在开启的线程个数
            # print(t2.isAlive())#线程是否运行
            # t2.setName(123)#跟改线程名
            # print(t.getName())#查看线程名
        # t1.join()#等待线程结束,在往下走
        # t2.join()
        print("all over %s"%ctime())
    其他方法

    同步锁(也叫互斥锁):

    import time
    import threading
    
    def sub():
        global num
        temp=num
        time.sleep(0.00001)
        num=temp-1
    num=100
    
    l=[]
    
    for i in range(100):
        t=threading.Thread(target=sub)
        t.start()
        l.append(t)
    
    for i in l:i.join()
    print(num)
    资源会被破坏
    lock=threading.Lock(),加上同步锁。
    import time
    import threading
    
    def sub():
        global num
        lock.acquire()
        temp=num
        time.sleep(0.00001)
        num=temp-1
        lock.release()
    num=100
    
    l=[]
    lock=threading.Lock()
    for i in range(100):
        t=threading.Thread(target=sub)
        t.start()
        l.append(t)
    
    for i in l:i.join()
    print(num)
    数据部分成串行

    死锁和递归锁:

    在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。下面是一个死锁的例子:

    import threading
    import time
    
    class MyThresd(threading.Thread):#类的继承方式启动线程
        def actionA(self):
            A.acquire() #获得A锁
            print(self.name,"gotA",time.ctime())
            time.sleep(2)
            B.acquire() #获得B锁
    
            print(self.name,"gotB",time.ctime())
            time.sleep(1)
    
            B.release() #释放B锁
            A.release() #释放A锁
    
        def actionB(self):
            B.acquire()
            print(self.name,"gotA",time.ctime())
            time.sleep(2)
            A.acquire()
    
            print(self.name,"gotB",time.ctime())
            time.sleep(1)
    
            A.release()
            B.release()
    
        def run(self):
            self.actionA()
            self.actionB()
    if __name__ == '__main__':
        A=threading.Lock()  #产出A锁
        B=threading.Lock()  #产生B锁
        # r_lock=threading.RLock()  #递归锁,把A、B锁换成Rlock()
        l=[]
        for i in range(5):
            t=MyThresd()    #实例创建线程
            t.start()   #启动线程,执行run方法
            l.append(t) #对象加到列表里
        for i in l: #便利获取对象
            i.join()    #等待线程结束
        print("ending.....")
    死锁

    为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

    import threading
    import time
    
    class MyThresd(threading.Thread):#类的继承方式启动线程
        def actionA(self):
            r_lock.acquire() #获得A锁
            print(self.name,"gotA",time.ctime())
            time.sleep(2)
            r_lock.acquire() #获得B锁
    
            print(self.name,"gotB",time.ctime())
            time.sleep(1)
    
            r_lock.release() #释放B锁
            r_lock.release() #释放A锁
    
        def actionB(self):
            r_lock.acquire()
            print(self.name,"gotA",time.ctime())
            time.sleep(2)
            r_lock.acquire()
    
            print(self.name,"gotB",time.ctime())
            time.sleep(1)
    
            r_lock.release()
            r_lock.release()
    
        def run(self):
            self.actionA()
            self.actionB()
    if __name__ == '__main__':
        # A=threading.Lock()  #产出A锁
        # B=threading.Lock()  #产生B锁
        r_lock=threading.RLock()  #递归锁,把A、B锁换成Rlock()
        l=[]
        for i in range(5):
            t=MyThresd()    #实例创建线程
            t.start()   #启动线程,执行run方法
            l.append(t) #对象加到列表里
        for i in l: #便利获取对象
            i.join()    #等待线程结束
        print("ending.....")
    递归锁

    解死锁:acquire有一个计数,获得一把锁就加一,只要计数器一直大于0,其他线程就要一直等待

    同步条件(Event):

    event = threading.Event()

    event.wait()  #等待event被设定

    event.set()  #设定event

    event.clear()  #清空event的设定

    import threading,time
    class Boss(threading.Thread):
        def run(self):
            print("Boss:今晚大家都要加班到22;00")
            print(event.isSet())    #打印是否被设定默认是False
            event.set() #设定标志位
            time.sleep(4)
            print("22:00到了,下班吧!")
            event.set()
    
    class Worker(threading.Thread):
        def run(self):
            event.wait()    #阻塞等待,等待标志位被设定,设定就往下走
            print("太苦B了")
            # event.clear()   #清空标志位
            time.sleep(2)
            event.wait()    #清空event后,再次阻塞
            print("终于下班了")
    if __name__ == '__main__':
        event=threading.Event() #相当于是个标志位
    
        threads=[]
        for i in range(5):
            threads.append(Worker())    #创建5个线程对象
        threads.append(Boss())  #创建1个线程对象
        for t in threads:
            t.start()   #所有线程全部执行
        for t in threads:
            t.join()    #等待线程全部执行完
        print("ending.....")
    同步条件:event=threading.Event()

    信号量(Semaphore):

    import threading,time
    class MyThread(threading.Thread):
        def run(self):
            if semaphore.acquire(): #一次可以进来5把锁
                print(self.name)
                time.sleep(2)
                semaphore.release()
    
    if __name__ == '__main__':
        semaphore=threading.Semaphore(5)    #获取锁的对象,设置同时开启5个线程,默认是一个
    
        threads=[]
        for i in range(100):
            threads.append(MyThread())
        for t in threads:
            t.start()
        for t in threads:
            t.join()
    信号量(Semaphore):开启x个线程

    *多线程利器---队列(queue)

    列表是不安全的数据结构

    import threading,time,queue
    li=[1,2,5,3,4,5]
    
    print(li)
    def pri():
        while li :
            a=li[-1]
            print(a)
            time.sleep(1)
            # try:
            li.remove(a)
            # except Exception as e:
            #     print("-"*10,a,e)
    
    t1=threading.Thread(target=pri)
    t1.start()
    t2=threading.Thread(target=pri)
    t2.start()
    同一时刻两个线程取到了一个数字

    queue列队类的方法:

    创建一个“队列”对象
    import Queue
    q = Queue.Queue(maxsize = 10)  #参数代表列队能放多少个值
    Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
    
    将一个值放入队列中
    q.put(10)  #block默认为True,改为False则抛出异常
    调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
    1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
    
    将一个值从队列中取出
    q.get()  #block默认为True,改为Flase则抛出异常
    调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
    get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
    
    Python Queue模块有三种队列及构造函数:
    *1、Python Queue模块的FIFO队列先进先出。   class queue.Queue(maxsize)
    *2、LIFO类似于堆,即先进后出。               class queue.LifoQueue(maxsize)
    *3、还有一种是优先级队列级别越低越先出来。        class queue.PriorityQueue(maxsize)
    
    此包中的常用方法(q = Queue.Queue()):
    q.qsize() 返回队列的大小
    q.empty() 如果队列为空,返回True,反之False
    q.full() 如果队列满了,返回True,反之False
    q.full 与 maxsize 大小对应
    q.get([block[, timeout]]) 获取队列,timeout等待时间
    q.get_nowait() 相当q.get(False)
    非阻塞 q.put(item) 写入队列,timeout等待时间
    q.put_nowait(item) 相当q.put(item, False)
    q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
    q.join() 实际上意味着等到队列为空,再执行别的操作

    其他模式:

    import queue
    
    #先进后出
    
    q=queue.LifoQueue()
    
    q.put(34)
    q.put(56)
    q.put(12)
    
    #优先级
    # q=queue.PriorityQueue()
    # q.put([5,100])
    # q.put([7,200])
    # q.put([3,"hello"])
    # q.put([4,{"name":"alex"}])
    
    while 1:
    
      data=q.get()
      print(data)

    生产者消费者模型:

    为什么要使用生产者和消费者模式

    在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

    什么是生产者消费者模式

    生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

    这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个结耦的过程。

    import time,random
    import queue,threading
    
    q=queue.Queue()
    
    def Producer(name):
        count=0
        while count < 10:
            print("making......")
            time.sleep(2)
            q.put(count)
            print("Producer %s has produced %s baozi.."%(name,count))
            count+=1
            # q.task_done()   #发信号,放了一个数据
            q.join()
            print("ok......")
    
    def Consumer(name):
        count=0
        while True:
            time.sleep(4)
            data = q.get()  # 接收列队的数据
            print("eating.....")
            # q.join()    #队列有数据了才往下走
            q.task_done()
    
            print("33[32;1mconsumer %s has eat %s baozi...33[0m"%(name,data))
            # else:
            #     print("----no baozi anymore-----")
            count+=1
    
    p1=threading.Thread(target=Producer,args=("  A  ",))
    c1=threading.Thread(target=Consumer,args=("  B  ",))
    c2=threading.Thread(target=Consumer,args=("  C  ",))
    c3=threading.Thread(target=Consumer,args=("  D  ",))
    p1.start()
    c1.start()
    c2.start()
    c3.start()

     进程

    调用的方法:from multiprocessing import Process

    进程的调用1:创建
    from multiprocessing import Process
    import time
    def f(name):
        time.sleep(1)
        print('hello', name,time.ctime())
    
    if __name__ == '__main__':
        p_list=[]
        for i in range(3):
            p = Process(target=f, args=('alvin',))
            p_list.append(p)
            p.start()
        for i in p_list:
            i.join()
        print('end')
    进程的调用2:继承
    from multiprocessing import Process
    import time
    
    class MyProcess(Process):
        def __init__(self):
            super(MyProcess, self).__init__()   #继承父类的__init__方法
            #self.name = name
    
        def run(self):
            time.sleep(1)
            print ('hello', self.name,time.ctime())
    
    if __name__ == '__main__':
        p_list=[]
        for i in range(3):
            p = MyProcess() #拿到进程对象
            p.daemon=True
            p.start()
            p_list.append(p)
    
        # for p in p_list:
        #     p.join()    #等待子进程没执行完,主进程就要等待
        print('end')

     要显示单个进程ID,这里是一个扩展的例子:os.getppid(),os.getpid()

    from multiprocessing import Process
    import os
    import time
    
    def info(title):
        print("title:", title)
        print('parent process:', os.getppid())  #程序运行的父进程的id号
        print('process id:', os.getpid())   #程序运行的进程的id号
    
    def f(name):
        info('function f')
        print('hello', name)
    
    if __name__ == '__main__':
        info('main process line')
        time.sleep(1)
        print("------------------")
        p = Process(target=info, args=('yuan',))
        p.start()
        p.join()
    要显示单个进程ID,这里是一个扩展的例子:






  • 相关阅读:
    阿里云服务器ECS centos_7 安装jdk环境
    Spring Data Jpa 投影(Projection)的用法
    win10 docker 部署微服务
    centos 安装docker
    VMware 启动 虚拟机后一直黑屏
    VMware 新建虚拟机选择操作系统的时候提示,此主机不支持64位客户机操作系统,此系统无法运行
    mysql 表名全变小写的解决(windows)
    docker: Error response from daemon: driver failed programming external connectivity on endpoint jovial_morse (71d0315110605c3812f7255c7072f5d38512118a4635feaf84cdc170d3bee8d1): Error starting userland
    win10 docker部署springboot项目
    nginx failed (1113: No mapping for the Unicode character exists in the target multi-byte code page)
  • 原文地址:https://www.cnblogs.com/shizhengwen/p/6364675.html
Copyright © 2020-2023  润新知