• python学习笔记(32)多线程&多进程


    一、多线程&多进程

      对于操作系统来说,一个任务就是一个进程。比如我在电脑上打开视频看电视,再启动QQ,这样打开视频和启动QQ就是两个进程了 。进程是多个资源的集合

      每个进程中可以做很多事情,比如我打开QQ,可以与A打字聊天,同时还可以与B视频,接收C的文件,一个进程中可以有很多线程来干活,这样一个QQ需要运行多个子任务,我们把这些子任务叫做 线程(thread)

      每个进程中至少有一个线程在干活,比如我打开QQ,即使不做任何操作,还是保留了一个QQ窗口,这就是主线程,子线程会等着主线程来调

      我们在做事情的时候,一个人做是比较慢的,如果多个人一起来做的话,就比较快了,程序也是一样的,我们想运行的速度快一点的话,就得使用多进程,或者多线程,在python里面,多线程被很多人诟病,为什么呢,因为Python的解释器使用了GIL的一个叫全局解释器锁,它不能利用多核CPU,

      只能运行在一个cpu上面,但是你在运行程序的时候,看起来好像还是在一起运行的,是因为操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。

      表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。这个叫做上下文切换。  

    ·  当需要使用大量网络IO、磁盘IO,不停的读写磁盘等(如存数据库)操作时,最好使用多线程来操作

       当需要使用大量消耗CPU性能时,比如排序,复杂计算等,最好使用多进程,因为多进程才能同时使用更多的cpu来进行处理数据

      多线程,线程之间的数据是共享的

      多进程,每个进程之间的数据是独立的

      

    二、python中通过threading模块来实现多线程

      简单启动线程的例子

    import threading
        import time
        def sayhi(num): #定义每个线程要运行的函数
         
            print("running on number:%s" %num)
         
            time.sleep(3)
    
    
    case_result = []
         def run_case(casename):
              case_result.append({casename:'success'})
         
    #多线程运行函数时,函数的返回结果拿不到,需要定义一个list将结果append进去
    
        if __name__ == '__main__':
            t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
            t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
            t1.start() #启动线程
            t2.start() #启动另一个线程
    

      

      

      

    主线程,也就是程序一开始运行的时候,最初的那个线程
    子线程,通过thread类实例化的线程,都是子线程
    如果加了线程等待,主线程等待子线程,执行结束后,主线程再去做别的操作。否则主线程在启动子线程后,不会等待子线程结束,而是继续走主线程代码

    下面举例说明两种线程等待的方式
    import threading
    import time
    def insert_db():
        time.sleep(3)
        print('insert_db over')
    
    #串形的方式,非多线程
    # start_time = time.time()
    # for i in range(3):
    #     insert_db()
    # end_time = time.time()
    # print('串行的执行的时间',end_time - start_time )
    
    threads = []
    start_time2 = time.time()
    
    #1、第一种方式,麻烦一点
    # for i in range(3):
    #     t = threading.Thread(target=insert_db)#有入参时,需要加args=()或者args=[]来传递参数,当只有一个入参时元组记得加逗号,args=(1,)
    #     t.start()
    #     threads.append(t)
    #
    # for t in threads:
    #     t.join()#使用join实现主线程等待子线程结束
    
    #2、第二种方式,思路是判断当前存活的线程个数
    for i in range(3):
        t = threading.Thread(target=insert_db)
        t.start()
    #当线程数为1,即子线程结束,代码才往下走,否则在死循环中
    while threading.activeCount()!=1:#活着的线程数
        pass
    
    end_time2 = time.time()
    print('多线程执行的时间',end_time2 - start_time2)
    

      

    例子:通过多线程实现下载QQ头像

     1 import time
     2 
     3 import requests,hashlib,threading
     4 
     5 def down_load_pics(url):
     6     req = requests.get(url)
     7     title = hashlib.md5(url.encode())
     8     with open(title.hexdigest()+'.png','wb') as fw:
     9         fw.write(req.content)
    10 
    11 url_list = ['https://q4.qlogo.cn/g?b=qq&nk=1345741814&s=140',
    12         'https://q4.qlogo.cn/g?b=qq&nk=1134900814&s=140',
    13         'https://q4.qlogo.cn/g?b=qq&nk=511402865&s=140'
    14        ]
    15 
    16 start_time = time.time()
    17 for url in url_list:
    18     t = threading.Thread(target=down_load_pics,args=(url,))
    19     t.start()
    20 
    21 while threading.activeCount()!=1:#等待子线程结束
    22     pass
    23 
    24 end_time = time.time()
    25 print(end_time - start_time)
    守护线程:当主线程结束后,守护线程会自动结束。就比如秦始皇死后,所有为他建造皇陵的人全部都要去陪葬。那么秦始皇就是主线程,其他陪葬的都是子线程。

    以一个视频聊天为例,关闭QQ后,所有的子线程会停止
    #守护线程,一但主线程死掉,那么守护线程不管有没有执行完成,全都结束
    import threading
    import time
    def talk(name):
        print('正在和%s聊天'%name)
        time.sleep(200)
    
    def shipin(name):
        print('正在和%s视频'%name)
        time.sleep(300)
    
    print("qq主窗口")
    t1 = threading.Thread(target=talk,args=['刘小燕'])
    t1.setDaemon(True) #设置线程为守护线程
    t1.start()
    
    t2 = threading.Thread(target=shipin,args=['蔡明昌'])
    t2.setDaemon(True) #设置线程为守护线程
    t2.start()
    
    time.sleep(5)
    
    print('结束。。。')
    

      

    线程锁: 

      经常出现在数据库操作时,如果多线程同时操作一个数据库操作时,由于线程有快慢先后的区别,原来的数据处理完可能会出错。那么在一个线程来的时候,加上一把锁,只有等这个线程干活结束后,再把锁打开,下一个线程再去操作数据干活,这样数据就不会乱套了

      

    import threading
    count = 0
    lock = threading.Lock()#申请一把锁
    def test():
        global count
        print(threading.current_thread())
        '''第一个加锁方式,不解锁后出现线程死锁'''
        # lock.acquire()#加锁
        # count+=1
        # lock.release()#解锁
        '''第二种加锁方式,会自动解锁'''
        with lock:
            count+=1
    
    for i in  range(3):
        t = threading.Thread(target=test)
        t.start()
    

      

      

    三、进程通过multiprocessing模块实现

      进程和线程用法和线程相似,都有等待、锁、等操作,一个进程内部可以再启动多个线程

      进程原来是使用多个CPU来处理,每个进程之间是独立的,处理的数据是相互独立,而线程处理的数据是共享的,所以多进程的锁意义不大。

      

    import multiprocessing
    import time
    import threading
    
    lock = multiprocessing.Lock()
    a =1
    
    def xxx():
        pass
    def mytest():
        '''进程中再启20个线程调用xxx方法'''
        for i in range(20):
            t = threading.Thread(target=xxx)
            t.start()
    
        time.sleep(3)
        global a
        with lock:#加锁并自动解锁
            a+=1
        print('over')
    
    # process = []
    #启动进程需要放在main中
    if __name__ == '__main__':
        for i in range(5):
            p = multiprocessing.Process(target=mytest,name='tyl')
            p.start()
            # process.append(p)
        # print(p.pid)#进程id
    
        #主进程等待子线程结束方法1:
        while len(multiprocessing.active_children())!=0:
            pass
    
        #主进程等待子线程结束方法2:
        # for x in process:
        #     x.join()
    

      





  • 相关阅读:
    python自定义编写有关用户登录注册程序代码
    项目经理多年的经验之谈
    Linux虚拟机克隆后,启动系统发现网卡无法启动
    mysql出现服务器异常后,重启服务器后无法开启数据库处理方法
    计算机毕业四年,我都做了什么?
    个人Blog(采用Django+uwsgi+nginx)里面包含很多技术文章
    Delphi to C# Equivalancesdelphi和C#类似的地方
    Javascript浏览器关于scrollLeft,scrollTop的兼容性
    Mozilla Firefox15怎么样才能把标签页弄到下面去,就和360的一样,Mozilla Firefox15没有取消标签置顶这个选项……
    Delphi过程函数传递参数的几种方式
  • 原文地址:https://www.cnblogs.com/bugoobird/p/13125830.html
Copyright © 2020-2023  润新知