zoukankan      html  css  js  c++  java
  • Python学习--多线程&多进程

    一、线程&进程

    1、进程

    在系统中,一个任务就是一个进程。比如开启浏览器,打开微信,打开两个记事本就是启动了两个记事本进程。每打开一个任务,代表在系统中启动了一个进程,进程代表着一个资源的集合

    2、线程(Thread)

    线程是操作系统能够运行的最小度量单位,他被包含在进程当中,是进程中实际的运行单位。

    有些进程不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)

    由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。线程是最小的执行单元,而进程由至少一个线程组成。

    比如我们做事一个人做是比较慢的,如果多个人一起来做的话,就比较快了。程序也是一样的,我们想运行的速度快一点的话,就得使用多进程,或者多线程,在python里面,多线程被很多人诟病,为什么呢,因为Python的解释器使用了GIL的一个叫全局解释器锁,它不能利用多核CPU,只能运行在一个cpu上面,但是你在运行程序的时候,看起来好像还是在一起运行的,是因为操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。这个叫做上下文切换。

    3、进程与线程的区别

    进程 : 对各种资源管理的集合

    线程 : 操作系统最小的调度单位,是一串指令的集合

    进程不能单独执行,它只是资源的一个集合,如果进程想要操作CPU,进程必须先创建一个线程,在进程中的所有线程,都同享同一块内存空间。进程中第一个线程是主线程,主线程创建其他线程,其他线程也可以创建线程,线程之间是平等的,进程有父进程、子进程,独立的内存空间,唯一的进程标识符【pid】(注:启动线程比启动进程快,线程的内存空间是共享的,进程的空间是独立的。)

    二、多线程的使用(threading模块)

    import threading
    import time
    
    def talk(name, age): #定义每个线程要运行的函数
        print('%s说:今年我%s岁' % (name, age))
        time.sleep(5)
        print('%s说:介绍完毕' % name)
    
    # def talk(*args, **kwargs):
    #     print('%s说:今年我%s岁' % (args[0], kwargs['age']))
    #     time.sleep(2)
    #     print('%s说:介绍完毕' % args[0])
    
    t1 = threading.Thread(target=talk, args=('小黑','19')) #生成一个线程实例
    t2 = threading.Thread(target=talk, args=('阿根',),kwargs={'age': 45}, name='线程2',group=None)
    # target代表你要启动的多线程运行的函数,把函数名赋值给target即可
    # args代表多线程执行的函数所需要的参数,
    # 注:这里args如果传递多个参数时,它接收的是元组,如果是一个参数时,我们需要写逗号
    # kwargs同样是多线程执行时函数所需要的参数(接受的是一个字典)
    # name代表这个线程的名字,
    # group代表线程组(Python还没有实现,所以默认必须穿None或不写)
    
    t1.start() #启动线程
    t2.start() #启动另一个线程

    还有一种通过继承启动多线程(两种方式没有区别只是两种写法,这种更复杂了解就好)

    通过继承threading.Thread并覆盖run方法完成多线程,比我们通过def实现的多继承有局限性,我们在类中定义的run方法,不是随意定义的,函数名必须为run,否则不会重写Thread的run方法,程序不会执行多线程。

    import threading
    import time
     
    class MyThread(threading.Thread):
        def __init__(self, name):
            super(MyThread, self).__init__()
            self.name = name
     
        def run(self):
            print(self.name, '说开始')
            time.sleep(2)
            print(self.name, '说结束')
     
    t1 = MyThread('小黑')
    t2 = MyThread('阿根')
    t1.start()
    t2.start()

    threading与实例对象提供了几个方法:

    threading.active_count()  # 返回当前运行的线程个数
     
    threading.enumerate()  # 返回当前运行中的线程list
     
    threading.current_thread()  # 返回当前的线程变量
     
    t1.start()  # 启动线程
     
    t1.is_alive()  # 判断线程是否在运行 运行指启动后、终止前。
     
    t1.getName()  # 获取线程名
     
    t1.setName('填写更改后的名称')  # 对线程进行命名
     
    t1.setDaemon(True)  # 设置守护线程
     
    t1.isDaemon()  # 判断是否是守护线程
     
    t1.join(timeout=20)  # 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)

    三、线程等待

    多线程在运行的时候每个线程都是独立运行的,不受其他的线程干扰。如果想在哪个线程运行完之后,再做其他操作的话,就得等待它完成,使用join,等待线程结束

    import threading
    import time
    
    def talk(name):
        print(name, '开始了')
        time.sleep(2)
        print(name, '结束了')
    
    t_list = []
    for i in range(1, 4):
        t = threading.Thread(target=talk, args=('小黑%s' %i,))
        t.start()
        t_list.append(t)
    start = time.time()
    for t in t_list:
        t.join()
    end = time.time() - start
    print(end)
    print(threading.active_count())

    四、守护线程 

    守护线程,当主线程执行完成后,所有守护线程立即结束执行。例:一个国王(非守护线程)有很多仆人(守护线程),国王死后它的仆人也得跟着陪葬

    #------守护线程-------
    #守护线程就是和秦始皇陪葬的人一样
    #主线程就是秦始皇
    #子线程就是陪葬的人。
    
    import threading,time
    
    def run():
        time.sleep(9)
        print('run。。。')
    
    for i in range(10):
        t = threading.Thread(target=run)
        t.setDaemon(True) #设置子线程成为一个守护线程
        t.start()
    print('over..')
    #结果是:over..
    #当主线程执行完后,不管子线程结没束结束都跟着主线程一起结束了
    
    #实际场景:qq、浏览器,关闭浏览器时所有的tab页都会被关闭
    #在实际写代码的过程中还没有发现有需要用守护线程的场景

    五、线程锁

    # 线程锁就是:
    # 很多线程一起在操作一个数据的时候,可能会有问题,
    # 就要把这个数据加个锁,同一时间只能有一个线程操作这个数据。
    
    import threading
    from threading import Lock
    
    num = 0
    lock = Lock() #实例化一把锁,先申请一把锁
    def run():
        global num
        # lock.acquire() #加锁
        # num+=1
        # lock.release()  #解锁,上锁后一定要记得将锁解开,否则后面的线程无法操作同一份资源,就会导致死锁
     with lock: #自动加锁解锁,python3会自动加锁,但为了保险最好还是加上锁;python2里面就需要手动加锁 num+=1 for i in range(100): t = threading.Thread(target=run) t.start() while threading.active_count()!=1: pass #判断当前活动的线程是几个,如果是1的话, #说明子线程都已经执行完成了。 print(num)

    练习:通过一个简单的爬虫,看下多线程的效果

    import threading
    import requests, time
    
    urls = {
        "baidu": 'http://www.baidu.com',
        "blog": 'http://www.nnzhp.cn',
        "besttest": 'http://www.besttest.cn',
        "taobao": "http://www.taobao.com",
        "jd": "http://www.jd.com",
    }
    
    def run(name, url):
        res = requests.get(url)
        with open(name + '.html', 'w', encoding=res.encoding) as fw:
            fw.write(res.text)
    
    start_time = time.time()
    lis = []
    for url in urls:
        t = threading.Thread(target=run, args=(url, urls[url]))
        t.start()
        lis.append(t)
    for t in lis:
        t.join()
    end_time = time.time()
    print('run time is %s' % (end_time - start_time))
    
    # 下面是单线程的执行时间
    # start_time = time.time()
    # for url in urls:
    #     run(url,urls[url])
    # end_time = time.time()
    # print('run time is %s'%(end_time-start_time))

    六、多进程(multiprocessing)

    多进程要比多线程耗费的资源大,进程的数据是独立的,所以每启动一个进程都会生成一份内存来存储进程的数据。多进程实质就是启动每个进程中的默认线程去完成任务。

    # Python里面的多线程,是不能利用多核CPU的,
    # 如果想利用多核CPU的话,就得使用多进程,python中多进程使用multiprocessing模块
    
    from multiprocessing import Process
    import time
    
    def my(name):
        time.sleep(3)
        print('hello',name)
    
    p = Process(target=my,args=('hei',)) #方式与多进程相同
    p.start()
    p.join

    七、进程池

    # 进程池用来快速启动几个进程,使用进程池的好处的就是他会自动管理进程数,
    # 咱们只需要给他设置一个最大的数就ok了。有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;
    # 但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务
    
    from multiprocessing import Pool
    import os
    
    def worker(msg):
        print("%s开始执行,进程号为%d" % (msg, os.getpid()))
    
    
    if __name__ == '__main__':
    
        po = Pool(3)  # 定义一个进程池,最大进程数3
        for i in range(0, 10):
            # Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))
            # 每次循环将会用空闲出来的子进程去调用目标
            po.apply_async(func=worker, args=(i,))
        # 第一个func参数指定运行的函数,第二个args是参数,没有参数可以不写
        print("----start----")
        po.close()  # 关闭进程池,关闭后po不再接收新的请求
        po.join()  # 等待po中所有子进程执行完成,必须放在close语句之后
        print("-----end-----")
  • 相关阅读:
    你有没有发现你的文章被人侵权?推荐一个工具给你
    带你找到五一最省的旅游路线【dijkstra算法代码实现】
    【最短路径Floyd算法详解推导过程】看完这篇,你还能不懂Floyd算法?还不会?
    Floyd算法java实现demo
    【销售系统设计01】关于线上与线下销售业绩冲突处理
    jenkins maven 自动远程发布到服务器,钉钉提醒团队
    研究windows下SVN备份及还原恢复方案
    xamarin.android SurfaceView 实现 游戏 触摸摇杆
    C++ 头文件和源文件 和 编译流程
    十大经典排序算法总结(JavaScript描述)
  • 原文地址:https://www.cnblogs.com/ddxxn/p/11476984.html
Copyright © 2011-2022 走看看