开发者

Python多线程即相关理念详解

目录
  • 一、什么是线程?
  • 二、开启线程的两种方式
    • 1、方式1
    • 2、方式2
  • 三、线程对象的jion方法()
    • 四、 补充小案例
      • 五、守护线程
        • 六、线程互斥锁
          • 七、GTL-全局解释器
            • 八、验证多线程与多线程运用场景
              • 总结:

                一、什么是线程?

                线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程。车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是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()) # 统计当前活跃的进程数
                

                '''www.cppcns.com

                运行结果:

                子 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__ == '__mainwww.cppcns.com__':
                    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__'www.cppcns.com:
                    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密集型更适合多线程

                总结:

                http://www.cppcns.com线程和多进程都各自有各自的优势

                并且在后面的项目中通常可以多进程下面再开设多线程

                这样的话我们可以利用多核也可以节省资源消耗

                本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

                0

                上一篇:

                下一篇:

                精彩评论

                暂无评论...
                验证码 换一张
                取 消

                最新开发

                开发排行榜