欢迎光临
我们一直在努力

Python多线程即相关理念分析

这篇文章主要介绍“Python多线程即相关理念分析”,在日常操作中,相信很多人在Python多线程即相关理念分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python多线程即相关理念分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    一、什么是线程?

    线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程。车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

    总结进程与线程区别:

    '''
    进程:资源单位
    线程:执行单位
    线程才是真正干活的人,干活中需要的资源由线程所在进程提供
    每个进程肯定自带一个线程
    每个进程内可创建多个线程
    '''
    '''
    开进程:
        申请空间
        拷贝代码
        消耗资源大
    开线程:
        同一个进程内创建多个线程,无需上述两种操作,消耗资源相对较小
    '''

    多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

    二、开启线程的两种方式

    1、方式1

    from threading import Thread
    import time
    # 方法一
    def task(name):
        print('%s is running' % name)
        time.sleep(1)
        print('%s is over' % name)
    # 开启线程不需要在main下面执行代码,直接书写就可以
    # 但是习惯性的将启动命令写在main下面
    t = Thread(target=task, args=('egon',))
    t.start()  # 创建线程的开销非常小,几乎是代码一执行就已经创建了
    print('主')
    '''

    运行结果:
    egon is running

    egon is over
    '''

    2、方式2

    from threading import Thread
    class MyThread(Thread):
        def __init__(self, name):
            # 重写了别人的方法,又不知道别人的方法里有啥,就调用父类的方法
            super().__init__()
            self.name = name
        def run(self):
            print('%s is running' % self.name)
            time.sleep(1)
            print('%s is over' % self.name)
    if __name__ == '__main__':
        t = MyThread('egon')
        t.start()
        print('主')
    '''

    运行结果:
    egon is running

    egon is over
    '''

    三、线程对象的jion方法()

    看过我讲解进程文章的小伙伴想必都知道jion的功能,线程的jion方法于进程的jion方法功能类似-等待一个线程执行完毕后再执行下一个线程

    from threading import Thread
    def task(name):
        print('%s is running' % name)
        time.sleep(1)
        print('%s is over' % name)
    if __name__ == '__main__':
        t=Thread(target=task,args=('egon',))
        t.start()
        t.join()# 主线程等待子线程运行结束后再执行
        print('主')

    '''
    运行结果:
    egon is running
    egon is over

    '''

    补充一个知识点:同一个进程下的多个线程数据共享,下面为大家举一个简单的案例

    from threading import Thread
    money=100
    def task():
        global money
        money=66
    if __name__ == '__main__':
        t=Thread(target=task,args=())
        t.start()
        print(money)

    # 结果:66

    四、 补充小案例

    from threading import Thread
    import os,time
    def task():
        print('子 pid:',os.getpid())
    if __name__ == '__main__':
        t=Thread(target=task,args=())
        t.start()
        print('主 pid:',os.getpid())
        # 两个线程的pid号一样,说明在同一个进程下

    '''
    运行结果:
    子 pid: 13444
    主 pid: 13444
    '''

    # 这是个容易混淆的案例
    from threading import Thread,current_thread,active_count
    import os,time
    def task(n):
        print('子',current_thread().name)
        time.sleep(n) # 延长线程存活时间
    if __name__ == '__main__':
        t=Thread(target=task,args=(1,))
        t1=Thread(target=task,args=(1,))
        t.start()
        t1.start()
        t.join()
        # print('主',current_thread().name)# 获取线程名字
        print(active_count()) # 统计当前活跃的进程数

    '''
    运行结果:
    子 Thread-1
    子 Thread-2
    1
    '''
    # 这里大家容易以为是3,其实运行后只有一个线程在活跃了,其它两个线程运行完后就停止运行了

    五、守护线程

    守护线程与守护进程的概念也类似,其实大家也能注意到,进程与线程有许多知识点即用法都是相通的,理解了一个另一个也是差不多的道理

    1、守护线程会随着主线程的结束而结束

    2、主线程运行结束后不会立刻结束,会等待所有的其它非守护线程结束后才会结束

    3、因为主线程的结束意味着所在进程的结束

    from threading import Thread
    import time
    def task(name):
        print('%s is running'%name)
        time.sleep(1)
        print('%s is over'%name)
    if __name__ == '__main__':
        t=Thread(target=task,args=('egon',))
        t.daemon=True #将t设置为守护线程
        t.start()
        print('主')

    '''
    运行结果:
    egon is running

    '''

    # 稍微有点迷惑性的例子
    from threading import Thread
    import time
    def foo():
        print('1234')
        time.sleep(1)
        print('end1234')
    def func():
        print('5678')
        time.sleep(3)
        print('end5678')
    if __name__ == '__main__':
        t1=Thread(target=foo,args=())
        t2=Thread(target=func,args=())
        t1.daemon=True # t1设为守护线程,t2为非守护线程
        t1.start()
        t2.start()
        print('主......')

    '''
    运行结果:
    1234
    5678主……
    end1234
    end5678
    '''

    '''
    因主线程会等待非守护线程运行结束后在结束,
    所有主线程会等待t2(非守护线程)结束再结束,
    '''

    六、线程互斥锁

    多个线程操作同一份数据的时候,会出现数据错乱的问题

    针对上述问题,解决方式就是加锁处理

    from threading import  Thread,Lock
    import time
    money=100
    mutex=Lock()
    def task():
        global money
        mutex.acquire()
        tmp=money
        time.sleep(0.1)# 模拟网络延迟
        money=tmp-1
        mutex.release()
    if __name__ == '__main__':
        t_list=[]
        for i in range(100):
            t=Thread(target=task,args=())
            t.start()
            t_list.append(t)
        for t in t_list:
            t.join()
        print(money)

    # 运行结果:0
    # 多个人操作同一份数据,数据错乱,加锁处理

    七、GTL-全局解释器

    相信学python的小伙伴都知道,python解释器其实有多个版本

    • Cpython

    • Jpython

    • Pypython

    但是普遍使用的都是Cpython解释器

    在Cpython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行

    要注意同一进程下的多个线程无法利用多核优势!!!!

    想必大家心中也有不少疑惑:pyhon的多线程是不是一点用都没了呢????

    因为Cpython中的内存管理不是线程安全的。多线程并不是一无是处的,在遇到多IO操作的时候,多核的优势也会显示不出来,多进程与多线程的效率在该情况下差不了多少,而此时多进程相对浪费资源,多线程更加节省资源

    ps:内存管理就是垃圾回收机制:

    1、引用计数

    2、标记清除

    3、分带回收

    # GTL-全局解释器
    # 重点:1、GIL不是python的特点而是Cpython解释器的特点
    #      2、GIL是保证解释器级别的数据的安全
    #      3、GIL会导致同一个进程下的多个线程无法同时进行(即无法利用多核优势)
    #      4、针对不同的数据还是需要加不同的锁处理
    #      5、解释型语言的通病,同一个进程下多个线程无法利用多核优势

    多线程是否有用要看具体情况

    八、验证多线程与多线程运用场景

    # 计算密集型(CPU一直工作,也没有IO)(更适合多进程)
    from multiprocessing import Process
    from threading import Thread
    import os,time
    # 多进程情况
    def work():
        res=0
        for i in range(0,10000000):
            res*=i
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count())# 获取当前计算机CPU核数
        start_time=time.time()
        for i in range(8):# 我计算机是8核
            p= Process(target=work,args=())
            p.start()
            l.append(p)
        for p in l:
            p.join()
        print(time.time()-start_time)

    '''
    运行结果:
    8
    2.0726492404937744
    '''

    # 多线程情况
    from multiprocessing import Process
    from threading import Thread
    import os,time
    def work():
        res=0
        for i in range(0,10000000):
            res*=i
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count())# 获取当前计算机CPU核数
        start_time=time.time()
        for i in range(8):# 我计算机是8核
            t=Thread(target=work,args=())
            t.start()
            l.append(t)
        for p in l:
            p.join()
        print(time.time()-start_time)

    '''
    运行结果:
    8
    3.5790603160858154
    '''

    # 显然可知:计算密集型更时候多进程

    # IO密集型(任务一直有IO)(多线程更合适)
    from multiprocessing import Process
    from threading import Thread
    import os,time
    # 多线程
    def work():
        time.sleep(1)
    if __name__ == '__main__':
        l=[]
        start_time=time.time()
        for i in range(40):
            t=Thread(target=work,args=())
            t.start()
            l.append(t)
        for p in l:
            p.join()
        print(time.time()-start_time)
    # 运行结果:1.0205152034759521
    # 多进程
    from multiprocessing import Process
    from threading import Thread
    import os,time
    def work():
        time.sleep(1)
    if __name__ == '__main__':
        l=[]
        start_time=time.time()
        for i in range(40):
            p= Process(target=work,args=())
            # t=Thread(target=work,args=())
            # t.start()
            # l.append(t)
            p.start()
            l.append(p)
        for p in l:
            p.join()
        print(time.time()-start_time)

    # 运行结果:5.927189588546753

    # 显然可知:IO密集型更适合多线程

    到此,关于“Python多线程即相关理念分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注云网站,小编会继续努力为大家带来更多实用的文章!

    赞(0)
    【声明】:本博客不参与任何交易,也非中介,仅记录个人感兴趣的主机测评结果和优惠活动,内容均不作直接、间接、法定、约定的保证。访问本博客请务必遵守有关互联网的相关法律、规定与规则。一旦您访问本博客,即表示您已经知晓并接受了此声明通告。