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

    基础:

    什么是进程(process)?

    每一个程序的内存是独立的,例如:world不能访问QQ。 

    进程:QQ是以一个整体的形式暴露给操作系统管理,里面包含了各种资源的调用(内存管理、网络接口调用等)。启动一个QQ,也就是启动了一个进程。

    什么是线程(thread)?

    线程是操作系统能够进行运算调度的最小单位。线程包含在进程之中,是进程中的实际运作单位。

    一个进程中最少有一个线程

    一个线程时指 进程中一个单一顺序的控制流。

    一个进程中可以并发多个线程,每条线程并行执行不同的任务,线程与线程之间是相互独立的。

    线程和进程的区别:

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

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

    关系:

    进程中第一个线程是主线程(就是程序),主线程创建其他线程,其他线程也可以创建线程,线程之间是平等的;

    线程可以共享进程资源;

    线程之间数据可以共享,资源不共享,相互独立;

    主线程与其他线程并行执行,默认主线程可以不等待其他线程执行结束;

    进程有父进程、子进程,独立的内存空间,唯一的进程标识符,pid;

    什么是上下文切换?

    上下文切换,也称做进程切换或者任务切换,是指cpu从一个进程或线程切换到另一个进程或线程。举例说明,如下:

    a.开启QQ和微信,先聊QQ,然后切换到微信进行聊天,再切换到QQ,这个操作就叫做上下文切换。

    b.同时开启多个应用,电脑cpu配置是4核,多个应用之间进行切换时,没有卡顿现象 也完全感受不到cpu在进行任务切换,因为cpu处理很快,所以应用之间切换没有卡顿现象;

    单线程:

    import time
    import requests
    
    def get_res():
        urls = [
            'http://www.baidu.com',
            'https://www.taobao.com/',
            'https://www.jd.com/',
            'http://www.meilishuo.com/'
        ]
        start = time.time()
        for url in urls:
            print(url)
            resp = requests.get(url)
            print(resp)
        end = time.time()
        print('单线程运行时间:', end - start)

    执行结果,顺序执行:

    http://www.baidu.com
    <Response [200]>
    https://www.taobao.com/
    <Response [200]>
    https://www.jd.com/
    <Response [200]>
    http://www.meilishuo.com/
    <Response [200]>
    单线程运行时间: 1.0470597743988037

    解释:

    a. cpu顺序被请求

    b.除非cpu从一个url获取的响应,否则不会去请求下一个url

    c. 网络请求会花费较长的时间,所以cpu在等待网络请求的返回时间内一直处于闲置状态

     多线程:

    import time
    import threading
    
    def run(count):
        #每次执行该方法,需要休息2s
        time.sleep(2)
        print(count)
    
    #开始创建多线程
    start = time.time()
    for i in range(5):
        #创建线程,指定运行哪个函数,也就是指定哪个函数运行需要创建多线程
        #target=要运行的函数名
        # args=函数运行传入的参数,run方法需要传入count,把创建
        th = threading.Thread(target=run, args=(i, ))
        #启动线程
        th.start()
    #多线程创建完毕且运行结束
    end = time.time()
    print('运行时间:', end - start)

    运行结果:

    运行时间: 0.0
    1
    0
    4
    2
    3

    解释:

    a. 打印出来的运行时间统计的不是多线程的运行时间,因为没有运行run都要等待2s,所以多线程的运行时间至少为2s,那么打印的结果是什么?

      打印的运行时间是 主线程的运行时间,因为在运行python文件时,如果不启动多线程,至少有一个线程在运行

      线程与线程之间是相互独立的,最开始运行的是主线程,当运行到threading.Thread时,创建一个线程,创建的线程执行循环方,主线程执行其他操作

      主线程不等待其他线程结束后再结束

    b. 打印出的count数据是无序的,因为多线程运行run方法,并不是第一个请求结束后才进行下一个请求的,而是创建一个线程后执行run方法,接着创建另一个线程,哪个线程执行完毕就会打印出结果

    c. 总共创建了5个线程

    若想统计多线程总共的执行时间,也就是从开始创建线程 到 线程结束运行之间的时间(不需要考虑线程之间怎么运行的),操作如下:

    join()等待 (等待线程结束)

    • 子线程启动成功 
    • 主线程需要等待子线程运行结束,才执行后续代码
    import time
    import threading
    
    def run(count):
        #每次执行该方法,需要休息2s
        time.sleep(2)
        print(count)
    
    #开始创建多线程
    start = time.time()
    #存放创建的所有线程
    threads_list = []
    for i in range(5):
        #创建线程,指定运行哪个函数,也就是指定哪个函数运行需要创建多线程
        #target=要运行的函数名
        # args=函数运行传入的参数,run方法需要传入count
        th = threading.Thread(target=run, args=(i, ))
        #启动线程
        th.start()
        #把启动的每一个线程添加到线程组内
        threads_list.append(th)
    
    for t in threads_list:
        #主线程循环等待每个子线程运行完毕, t代表每个子线程
        t.join()  #等待线程结束
    
    #多线程创建完毕且运行结束
    end = time.time()
    print('运行时间:', end - start)

    执行结果:

    0
    1
    2
    4
    3
    运行时间: 2.0011146068573

     守护线程

    • 守护线程:主线程运行结束后,不管守护线程执行是否结束,都会结束,举例说明:
    • 比如皇帝有很多仆人,当皇帝死了之后,那么多仆人就得陪葬。
    • 只要非守护线程结束了,不管守护线程结束没结束,程序都结束
    • 必须在启动线程(start)前,设置守护线程
    • 子线程都为守护线程,守护主线程,主线运行结束或者退出前,都不会告知子线程
    import threading
    import time
    
    def run(count):
        time.sleep(2)
        print(count)
    
    for i in range(5):
        #循环创建线程,总共5个线程
        t = threading.Thread(target=run, args=(i, ))
        #设置守护线程,新创建的这些线程都是 主线程的 守护线程, 主线程创建一个线程后 就运行结束了
        t.setDaemon(True)
    #启动线程,守护线程设置必须在start前面
        t.start()
    print('over')

    GIL 全局解释器锁

    例如 4核机器上 
    Python创建4线程,四个线程均匀分到多核上,但是同时只能一核在处理数据。 
    python调用操作系统、C语音的原生接口,在出口做了设置。全局解释器锁,保证数据统一 
    所以有人说python的线程是假线程。 
    在修改数据的时候,为了防止数据改乱了,所以多线程就变成串行处理,但是以为是python在处理,实际上是调用了操作系统的C语音的线程接口,所以中间的过程,python控制不了了,只知道结果。在这种情况下,设置的方式是出口控制,虽然四个线程,但是同一时间只有一个线程在工作。 
      
    所以这算是python的一个缺陷,但是也不能说是python的缺陷,是Cpython的缺陷。因为Cpython是C语音写的,以后python的未来是PYPY。 

    线程锁

    线程锁,又叫互斥锁

    线程之间沟通:保证同一时间只有一个线程修改数据

    python2.x 中需要加锁,Python3.x中加不加锁都一样,因为解释器做了优化

    import threading
    from threading import Lock
    
    #创建lock对象
    num = 0
    lock = Lock()   #申请一把锁,创建锁的对象
    def run2():
        global num
        lock.acquire()      #修改数据前 加锁
        num += 1
        lock.release()      #修改后释放解锁
    
    lis = []
    for i in range(5):
        #创建线程
        t = threading.Thread(target=run2)
        #启动线程
        t.start()
        #将启动的线程添加到线程组内
        lis.append(t)
    
    for t in lis:
        #等待线程运行结束
        t.join()
    #num的值为5,执行多次后,会出现不一样的值
    print('over', num)

    RLock 递归锁

    大锁中还有小锁、递归锁,解锁时就混了,所以用递归锁,Rlock()

    import threading,time
    
    def run1():
        print("grab the first part data")
        lock.acquire()
        global num
        num +=1
        lock.release()
        return num
    def run2():
        print("grab the second part data")
        lock.acquire()
        global  num2
        num2+=1
        lock.release()
        return num2
    def run3():
        lock.acquire()
        res = run1()
        print('--------between run1 and run2-----')
        res2 = run2()
        lock.release()
        print(res,res2)
    
    if __name__ == '__main__':
    
        num,num2 = 0,0
        lock = threading.RLock()  # 声明递归锁
        # lock = threading.Lock() # 用互斥锁,会锁死了,弄混锁情况,可以试一下
        for i in range(10):
            t = threading.Thread(target=run3)
            t.start()
    
        while threading.active_count() != 1:
            print(threading.active_count())
        else:
            print('----all threads done---')
            print(num,num2)

     多线程的另一种写法:

    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()

    多进程(了解即可):

    python里面的多线程,是不能利用多核cpu的,如果想利用多核cpu的话,就得使用多进程

    多进程适用CPU密集型任务

    多线程适用io密集型任务

    from multiprocessing import Process
    def f(name):
        time.sleep(2)
        print('hello', name)
    
    if __name__ == '__main__':
        for i in range(10):
            p = Process(target=f, args=('niu',))
            p.start()
    转载请务必保留此出处:http://www.cnblogs.com/lhly/p/7163791.html

  • 相关阅读:
    tensorflow2.0 GPU和CPU 时间对比
    第一次使用FileZilla Server
    PremiumSoft Navicat 15 for Oracle中文破解版安装教程
    Unmapped Spring configuration files found. Please configure Spring facet or use 'Create Default Context' to add one including all unmapped files.
    ng : 无法加载文件 D: odejs ode_global g.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
    angular
    Github上优秀的go项目
    win10---file explore 中remove quick access folder
    react--useEffect使用
    linux---cat 和 grep 的妙用
  • 原文地址:https://www.cnblogs.com/lhly/p/7163791.html
Copyright © 2011-2022 走看看