zoukankan      html  css  js  c++  java
  • 多线程

    一,线程               ====> 线程由代码段,数据段,TCB段组成

      线程被称作轻量级的进程        

            GIL:全局解释锁(只有Cpython解释器才有)对于线程来说,同一时间只允许一个线程访问cpu,因为有了GIL,所以没有真正的并行

      进程是资源分配的基本单位,线程是计算机可执行基本单位

      线程不可以自己独立拥有资源,线程的执行,必须依赖于所属进程中的资源

      进程中必须至少应该有一个线程

      线程分用户级线程和内核级线程:

      用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行调度

      内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。

    二,     进程与线程的区别

          (1)cpu切换进程要比cpu切换线程 慢很多,在python中,如果IO操作过多的话,使用多线程最好了

        (2)在同一个进程内,所有线程共享这个进程的pid,

          也就是说所有线程共享所属进程的所有资源(包括进程中的全局变量)和内存地址

              (3)因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行,当你的任务是计算密集的情况下,使用多进程好

                 总结:在CPython中,IO密集用多线程,计算密集用多进程

         (4)关于守护线程和守护进程的事情(注意:代码执行结束并不代表程序结束)

               守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束      ======>    注意:父进程的代码执行完毕,守护进程就不执行

             守护线程:要么自己正常结束,要么根据父线程的执行结束而结束      ======>   注意:主线程的执行结束(包含普通线程执行结束),守护线程也结束

    三, 线程的使用方法

      (1)递归锁用来解死锁,在单个线程内每获取一次锁会加计数一次,,释放一次会减计数一次,为零时就释放完毕

    from threading import RLock
    import time
    def wc():
        r1.acquire()   #  小明获得厕所资源    注意这个锁是个整体,里面如果有小锁
        time.sleep(1)                         #那么小锁用完必须释放在大锁内
        print('小明在厕所解手')
        r2.acquire()                       #小明拿到纸
        print('小明拿到卫生纸了')
        r2.release()                       #小明释放纸
        r1.release()                #  小明释放厕所资源
    
    def pap():
        r2.acquire()
        time.sleep(1)
        print('小雪在厕所解手')
        r1.acquire()
        print('小雪拿到卫生纸了')
        r1.release()
        r2.release()
    
    
    if __name__ == '__main__':
        r1 = r2 = RLock()                   #配了两把锁
        p1 = Thread(target=wc,args=())      #开启一个上厕所的线程
        p2 = Thread(target=pap,args=())     #开启一个纸的线程
        p1.start()
        p2.start()
    View Code

      (2)定时器(定时去执行任务)

    from threading import Timer# 定时器
    
    
    def func():
        print('就是这么nb!')
    
    Timer(2.5,func).start()
    # Timer(time,func)
    # time:睡眠的时间,以秒为单位
    # func:睡眠时间之后,需要执行的任务
    View Code

      (3)信号量(一次性执行几个线程)

    from threading import Semaphore,Thread
    import time
    
    
    def func(sem,i):
        sem.acquire()
        print('第%s个人进入屋子'%i)
        time.sleep(2)
        print('第%s个人离开屋子'%i)
        sem.release()
    
    
    sem = Semaphore(20)
    for i in range(20):
        t = Thread(target=func,args=(sem,i))
        t.start()
    View Code

      (4)事件

    from threading import Thread,Event
    import time,random
    
    def conn_mysql(e,i):
        count = 1         #计数
        while count <= 3:
            if e.is_set():     #接收e.set()的值,如果事件是真则执行
                print('第%s个人连接成功!'%i)
                break
            print('正在尝试第%s次重新连接...'%(count))
            e.wait(0.5)
            count += 1
    
    def check_mysql(e):
        print('33[42m 数据库正在维护 33[0m')
        time.sleep(random.randint(1,2))
        e.set()
    
    
    if __name__ == '__main__':
        e = Event()                  #创建一个时间对象
        t_check = Thread(target=check_mysql,args=(e,))     #注意:将时间对象传给执行函数
        t_check.start()              #起动执行检查数据库的任务
        for i in range(10):
            t_conn = Thread(target=conn_mysql,args=(e,i))   #注意:将时间对象传给执行函数
            t_conn.start()           #启动执行10个线程对象去执行连接数据库任务
    View Code

      (5)条件(可以控制线程的多少,线程数执行完毕就结束)

    from threading import Thread,Condition
    import time
    # Condition涉及4个方法
    # acquire()
    # release()
    # wait()    是指让线程阻塞住
    # notify(int)  是指给wait发一个信号,让wait变成不阻塞
    #     int是指,你要给多少给wait发信号
    
    
    def func(con,i):
        con.acquire()
        con.wait()# 线程执行到这里,会阻塞住,等待notify发送信号,来唤醒此线程
        con.release()
        print('第%s个线程开始执行了!'%i)
    
    if __name__ == '__main__':
        con = Condition()
        for i in range(10):
            t = Thread(target=func,args=(con,i))
            t.start()
        while 1:
            con.acquire()
            num = int(input(">>>"))
            con.notify(num)# 发送一个信号给num个正在阻塞在wait的线程,让这些线程正常执行
            con.release()
    View Code

     四,,线程池

    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    #不管是ProcessPoolExecutor的进程池  还是Pool的进程池,回调函数都是父进程调用的。
    #  concurrent.futures 这个模块是异步调用的机制
    #  concurrent.futures 提交任务都是用submit
    #  for + submit 多个任务的提交
    #  shutdown 是等效于Pool中的close+join,是指不允许再继续向池中增加任务,然后让父进程(线程)等待池中所有进程执行完所有任务。
    #进程池和线程池不需要用启动start
    def func1(num):
        sum = 0
        for i in range(num):
            sum += i ** 2
        return sum
    
    def back_func1(res):             # 回调函数对数据二次处理,一般会写入文件截取处理后的数据,无法return返回值
        with open('aa','a',encoding='utf-8') as f1:
            f1.write(str(66 * (res.result()))+'
    ')
    
    #一,关于线程的使用事项方法
    if __name__ == '__main__':
        #(1)用map函数创建多个线程
        t = ThreadPoolExecutor(20)
        lis = []
        ret = map(func1,range(200))#,开启多个线程:相当于for循环加上submit,,返回的是个生成器,用for循环去取值
        for i in ret:
            lis.append(i)
        print(len(lis),lis)
        #(2)用submit函数创建多个线程
        t1 = ThreadPoolExecutor(20)
        for i in range(200):
            t1.submit(func1,i).add_done_callback(back_func1)           #异步时,回调函数被主线程
            # ret = t.submit(func1,i)       #开启一个线程第一个参数是函数,第二个是参数(是个元组类型,args = (i,))可以简写
            # print(ret.result())            #注意返回值:用ret.result()来取值
        t1.shutdown()                       #这个命令的功能相当于t.close()  和t.join() 的功能,等待子线程执行结束
    
    #二,关于进程的使用事项方法
        #(1)用map函数创建多个进程
    if __name__ == '__main__':
        #(1) 用sumit函数创建多个进程:
        p = ProcessPoolExecutor(5)
        for i in range(100):
            # p.submit(func1,i).add_done_callback()              #异步时,回调函数被主进程调用
            ret = p.submit(func1, args = (i,))   ##开启一个进程第一个参数是函数,第二个是参数(是个元组类型,args = (i,))可以简写
            print(ret.result())                    #注意返回值:用ret.result()来取值
        p.shutdown()                            # 这个命令的功能相当于t.close()  和t.join() 的功能,等待子进程执行结束
        #(2)用map函数创建多个进程:
        p1 = ProcessPoolExecutor(20)
        lis = []
        ret = map(func1,range(200))#,开启多个进程:相当于for循环加上submit,,返回的是个生成器,用for循环去取值
        for i in ret:
            lis.append(i)               #这个操作也相当于等待所有的任务执行结束  相当于执行t.shutdown()
        print(len(lis),lis)
    View Code

      

      

  • 相关阅读:
    Python开发之路-多进程、进程队列、事件驱动模型、IO模型前戏
    Python开发之路
    cookies,sessionstorage,localstorage的区别?
    Linux的nmon监控结果分析文件中网络分析NET
    长字符串写法
    go中语句为什么不用加分号;结束
    %v的使用
    设计模式--装饰器模式
    第一类值VS第二类值
    闭包
  • 原文地址:https://www.cnblogs.com/laogao123/p/9524941.html
Copyright © 2011-2022 走看看