zoukankan      html  css  js  c++  java
  • Python线程详解

    Python线程详解

        # 进程的三状态:就绪 运行 阻塞
        # multiprocessing模块
            # Process-开启进程
            # Lock - 互斥锁
                # 为什么要在进程中加锁
                    # 因为进程操作文件也会发生数据不安全
            # Queue -队列 IPC机制(Pipe,redis,memcache,rabbitmq,kafka)
                # 生产者消费者模型
            # Manager - 提供数据共享机制

    ------------------Process-开启进程----------
    可查看链接https://www.cnblogs.com/Marcki/p/10111927.html
    ---------线程学习--------
    1、启动线程start
    线程的异步

    2、开启多个子线程
    3、join方法
    4、测试
    进程和线程的开启效率差
    数据隔离还是共享
    5、守护线程


    1)启动线程start

    (1从线程导入线程 2实例化Thread,传递线程函数。3对象.start())

    ```
    import os
    from threading import Thread
    #multiprocessing完全是仿照threading类写的
    def func():
    print(os.getpid())
    #启动线程 start
    Thread(target=func).start()
    print('---->',os.getpid())
    -------------结果:
    5804
    ----> 5804

    ```
    #由上可知,1、主线程和子线程在同一个进程里,pid一致。2、在Windows里面不需要写if name=='main'了。3、先打印子线程里的内容,再打印主线程里内容,与进程相反,说明线程开启快,快到主线程还没执行下一句代码这个子线程就创建并执行了。

    2)线程是异步的,并发的

    ```

    import os,time
    from threading import Thread
    def func():
    print('start son thread')
    time.sleep(1)
    print('end son thread')
    Thread(target=func).start()
    print('start',os.getpid())
    time.sleep(0.5)
    print('end',os.getpid())
    --------------结果:
    start son thread
    start 5280
    end 5280
    end son thread

    ```

    3)开启多个子线程

    import os,time
    from threading import Thread
    def func():
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid())
    for i in range(3):
    Thread(target=func).start()
    ----------------结果:
    start son thread
    start son thread
    start son thread
    end son thread 6772
    end son thread 6772
    end son thread 6772

    4)往线程函数里传参,通过args,和进程一样。线程的调度仍然是操作系统决定的,谁的时间片到了就运行谁的

    ```

    import os,time
    from threading import Thread
    def func(arg):
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid(),arg)
    for i in range(3):
    Thread(target=func,args=(i,)).start()
    ------------结果:
    start son thread
    start son thread
    start son thread
    end son thread 7112 0
    end son thread 7112 1
    end son thread 7112 2

    ```

    5)1、主线程等待所有子线程结束之后才结束。2、主线程如果结束了,主进程也就结束了。

    import os,time
    from threading import Thread
    def func(arg):
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid(),arg)
    for i in range(3):
    Thread(target=func,args=(i,)).start()
    print("main")
    --------------结果:
    start son thread
    start son thread
    start son thread
    main #主线程代码执行完了,等待子线程结束
    end son thread 5416 0
    end son thread 5416 1
    end son thread 5416 2
    Process finished with exit code 0 #所有子线程结束,主线程才结束,然后主进程结束
    5)[1]join方法 阻塞 直到子线程执行结束
    #用join,那么线程对象和start就不要放在一起了,单执行对象.start()来开启
    import os,time
    from threading import Thread
    def func(arg):
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid(),arg)
    t=Thread(target=func,args=(0,))
    t.start()
    t.join()
    print("子线程执行结束!")
    ----------------------结果:
    start son thread
    end son thread 6756 0
    子线程执行结束!
    [2]创建多个子线程,t.join()在循环外,这时t代表for循环最后一个值3,所有只是阻塞最后一个创建的t对象。所以每次打印“子线程执行结束!”都是在“end son thread 4864 3”之后。但是这时执行慢的子线程就没被阻塞就开始执行主线程中代码了。
    import os,time
    from threading import Thread
    def func(arg):
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid(),arg)
    for i in range(4):
    t=Thread(target=func,args=(i,))
    t.start()
    t.join()
    print("子线程执行结束!")
    --------------结果:
    start son thread
    start son thread
    start son thread
    start son thread
    end son thread 4864 0
    end son thread 4864 2
    end son thread 4864 3
    子线程执行结束!
    end son thread 4864 1
    [3]保证所有子线程都结束之后再继续执行主线程中的代码
    #注:将所有线程对象都追加到列表,循环列表,将每个对象.join阻塞。所有线程对象执行结束然后才执行主线程中程序
    import os,time
    from threading import Thread
    def func(arg):
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid(),arg)
    li=[]
    for i in range(4):
    t=Thread(target=func,args=(i,))
    t.start()
    li.append(t)
    for t in li:t.join()
    print("子线程执行结束!")
    --------------结果:
    start son thread
    start son thread
    start son thread
    start son thread
    end son thread 6480 0
    end son thread 6480 1
    end son thread 6480 2
    end son thread 6480 3
    子线程执行结束!

    6)[1]使用面向对象的方式启动线程

    #注意:用自己类创建的对象记得start开启线程
    #1、继承Thread类2、定义run方法,run方法是线程执行函数
    import os,time
    from threading import Thread
    class MyThread(Thread):
    def run(self):
    print('start son thread')
    time.sleep(1)
    print('end son thread',os.getpid())
    for i in range(3):
    MyThread().start()
    ------------------结果:
    start son thread
    start son thread
    start son thread
    end son thread 6116
    end son thread 6116
    end son thread 6116
    [2]面向对象启动线程并往线程函数里传参
    #1、创建init方法,传参进入类中 2、执行父类的init方法,不执行报错入下
    import os,time
    from threading import Thread
    class MyThread(Thread):
    def init(self,name):
    super().init() #执行父类init方法在实例变量后面报错了,不知道怎么回事,有时间测试一下
    self.name=name

    def run(self):
        time.sleep(1)
        print('end son thread',os.getpid(),self.name)

    for i in range(3):
    MyThread("mcw").start()
    ------------------结果:
    end son thread 2060 mcw
    end son thread 2060 mcw
    end son thread 2060 mcw

    不执行父类init报错:
    assert self._initialized, "Thread.init() not called"
    AssertionError: Thread.init() not called

    [3]start执行的原理
    class Foo:
    def start(self):
    self.run()
    def run(self):
    print('run in Foo')
    class Son(Foo):
    def run(self):
    print('run in son')
    Son().start()
    ---------------结果:
    run in son
    [4]自定义类中把init方法去掉后,实例化的时候还带有传参报错
    t=MyThread("mcw")
    File "C:python3libthreading.py", line 780, in init
    assert group is None, "group argument must be None for now"
    AssertionError: group argument must be None for now

    7)线程里面的其它方法 查看线程id

    [1]用类来启动线程,类里面和外面查看线程的id。对象.ident self.ident。注意:在init里定义与父类相同的实例变量名,会被覆盖掉的。
    import os,time
    from threading import Thread
    class MyThread(Thread):
    def run(self):
    time.sleep(1)
    print(' thread id',self.ident)
    for i in range(3):
    t=MyThread()
    t.start()
    print("线程id",t.ident)
    [2]current_thread()在哪个线程里代表哪个线程。用函数来启动线程,查看线程id 。
    #当前线程current_thread()对象,记得加括号(),函数中调用对象
    import time
    from threading import current_thread,Thread
    def func(i):
    print('start son thread',i,current_thread(),type(current_thread()))
    print("线程函数中调用执行这个函数的线程对象的线程id:%s"%current_thread().ident)
    time.sleep(1)
    print('end son thread',)
    t=Thread(target=func,args=(1,))
    t.start()
    print("main",t,t.ident)
    --------------结果:
    start son thread 1 <Thread(Thread-1, started 4444)> <class 'threading.Thread'>
    线程函数中调用执行这个函数的线程对象的线程id:4444
    main <Thread(Thread-1, started 4444)> 4444
    end son thread

    from threading import current_thread,Thread
    def func():
    print('end son thread',current_thread().ident)
    t=Thread(target=func).start()
    print("main thread",current_thread().ident)
    ------------结果:
    end son thread 1936 #current_thread()在哪个线程里代表哪个线程
    main thread 4928
    [3]enumerate active_count
    from threading import current_thread,Thread,active_count,enumerate
    def func():
    print('end son thread',current_thread().ident)
    t=Thread(target=func).start()
    print(enumerate(),enumerate) #主线程和子线程两个。enumerate()表示当前运行的线程,
    print(active_count(),active_count) #active_count()代表当前运行的线程的个数,相当于len(enumerate())
    -------------------结果:
    end son thread 4324
    2
    [<_MainThread(MainThread, started 2460)>, <Thread(Thread-1, started 4324)>] <function enumerate at 0x006E1270>
    2 <function active_count at 0x006E11E0>
    [4]terminate 能结束进程 。
    在线程中不能从主线程结束一个子线程

    8)进程和线程的开启效率差。进程一般开cpu个数的1到2倍,开多了浪费系统资源

    import time
    from threading import Thread
    from multiprocessing import Process
    def func(a,b):
    c=a+b
    if name=="main":
    p_start=time.time()
    p_li=[]
    for i in range(100):
    p=Process(target=func,args=(i,i2))
    p.start()
    p_li.append(p)
    for p in p_li:p.join()
    print("进程:",time.time()-p_start)
    t_start=time.time()
    t_li = []
    for i in range(100):
    t=Thread(target=func,args=(i,i
    2))
    t.start()
    t_li.append(t)
    for t in t_li:t.join()
    print("线程:",time.time() - t_start)
    ----------------结果:
    进程: 6.1583521366119385
    线程: 0.01500082015991211 #几百倍速度差距

    9)线程共享进程中的数据。子线程在进程中共享数据,

    from threading import Thread
    n=100
    def func():
    global n #最好不要轻易修改全局变量
    n-=1
    t_li=[]
    for i in range(100):
    t=Thread(target=func)
    t_li.append(t)
    t.start()
    for t in t_li:t.join()
    print(n)
    -----------结果:
    0

    10)守护线程

    [1]没有守护线程的时候
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    t=Thread(target=son1)
    t.start()
    ---------结果:
    一直打印“in son1”
    [2]有守护线程的时候,线程开启后,后面没有运行一段时间的代码
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    t=Thread(target=son1)
    t.daemon=True
    t.start()
    ---------------结果:
    什么都没有打印
    #这说明后面没代码了,主线程结束守护线程就结束了
    [3]有守护线程的时候,线程开启后,后面有运行一段时间的代码time.sleep(3)
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    t=Thread(target=son1)
    t.daemon=True
    t.start()
    time.sleep(3)
    ----------结果:
    打印5次"in son1"后结束
    #这说明后面还有代码,主线程执行完代码结束后守护线程就结束了
    [4]
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    t=Thread(target=son1)
    t.daemon=True
    t.start()
    time.sleep(3.1)
    ---------结果:
    打印6次"in son1"后结束
    #和上面打印5次对比,5次是因为主线程执行的3秒内,守护线程一直在运行,守护线程0.5秒打印一次,3秒到了主线程结束守护线程3秒也就结束。守护线程每0.5秒后打印一次,3秒后因为主线程停止守护线程也停止所以没有打印第6次
    #这里将主线程运行时间+0.1,也就是主线线程运行时间大于3秒,那么守护线程也运行3秒以上,刚过3秒就运行到打印“in son1”,所以打印6次
    [5]
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    def son2():
    for i in range(5):
    time.sleep(1)
    print("in son2")
    t=Thread(target=son1)
    t.daemon=True
    t.start()
    Thread(target=son2).start()
    time.sleep(3)
    -----------结果:
    in son1
    in son2
    in son1
    .......
    "in son2"打印5次,"in son1"打印9次
    #这说明守护线程一直等到所有的线程都结束之后才结束的
    #这说明守护线程除了守护主线程的代码之外也会守护子线程
    #主线程代码执行完毕,主线程没有关闭。主线程等待所有子线程结束之后才结束。守护线程守护主线程,所以守护线程也要等所有子线程结束之后才结束。如果子线程都结束了,主线程还有程序在运行,那么守护线程还是在守护主线程。
    [6]
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    t=Thread(target=son1)
    t.daemon=True
    t.start()
    time.sleep(3)
    print('1')
    print('1')
    print('1')
    ----------结果:
    六次 “in son1”
    1
    1
    1
    #原因:为什么主线程有代码在运行的时候,守护线程没有做打印操作了呢,因为这里守护线程是0.5s打印一次,主线程打印3次“1”所花费的时间不足0.5s不够守护线程到达下一次打印的时间。为什么打印6次呢,因为主线程sleep3秒正好达到守护线程打印第6次的时间,在主线程第3秒到打印出第一个“1”这个时间之间,守护线程才打印出第6个“in son1”。time.sleep(3)后面没有代码,守护线程在第3秒是没有执行到打印就随着主线程停止而停止了的。
    [7]
    import time
    from threading import Thread
    def son1():
    while True:
    time.sleep(0.5)
    print("in son1")
    t=Thread(target=son1)
    t.daemon=True
    t.start()
    time.sleep(3)
    print('1')
    time.sleep(1)
    print('1')
    ---------------结果:
    六个“in son1” 然后:
    1
    in son1
    in son1
    1

    11)在多进程里启动多线程

    import os
    from multiprocessing import Process
    from threading import Thread

    def tfunc():
    print('tfunc线程',os.getpid())
    def pfunc():
    print('pfunc进程-->',os.getpid())
    Thread(target=tfunc).start()

    if name == 'main':
    Process(target=pfunc).start()
    ------------------结果:
    pfunc进程--> 9716
    tfunc线程 9716

    方法总结:
    开启进程执行进程函数
    进程函数里开启线程执行线程函数
    线程函数里执行线程运行的程序

  • 相关阅读:
    linux驱动开发学习一:创建一个字符设备
    如何高效的对有序数组去重
    找到缺失的第一个正整数
    .NET不可变集合已经正式发布
    中国人唯一不认可的成功——就是家庭的和睦,人生的平淡【转】
    自己动手搭建 MongoDB 环境,并建立一个 .NET HelloWorld 程序测试
    ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误
    自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试
    ServiceStack 介绍
    一步一步实战扩展 ASP.NET Route,实现小写 URL、个性化 URL
  • 原文地址:https://www.cnblogs.com/machangwei-8/p/10908000.html
Copyright © 2011-2022 走看看