zoukankan      html  css  js  c++  java
  • Python 中的线程-进程2

    原文:https://www.cnblogs.com/i-honey/p/7823587.html

    Python中实现多线程需要使用到 threading 库,其中每一个 Thread类 的实例控制一个线程。

    Thread类

    #类签名

    1
    2
    def __init__(self, group=None, target=None, name=None,
                     args=(), kwargs=None, *, daemon=None):

      简单介绍一些初始化参数:

    target: 指定线程由 run () 方法调用的可调用对象。默认为 None, 意味着不调用任何内容。

    name: 指定该线程的名称。 在默认情况下,创建一个唯一的名称。

    args: target调用的实参,元组格式。默认为 (),即不传参。

    daemon: 为False表示父线程在运行结束时需要等待子线程结束才能结束程序,为True则表示父线程在运行结束时,子线程无论是否还有任务未完成都会跟随父进程退出,结束程序。

    线程启动:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import threading
     
     
    def worker(arg):#线程执行的目标函数
        print("I'm working {}".format(arg))
        print("Fineshed")
     
    t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")#线程对象
    t.start()#启动线程
     
    运行结果:
    I'm working <_MainThread(MainThread, started 10936)>
    Fineshed

      上面例子中,当函数执行完之后,线程也就跟着退出了。

    线程的传参:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import threading
     
    def add(x,y):
        print(x+y)
     
    t = threading.Thread(target=add,args=(4,5))
    t.start()
     
    print("====end===")
    运行结果:
    9
    ====end===

      线程的传参和函数传参没有区别,只需要注意传入的必须为元祖格式。

    线程退出:

    如果线程中任务是无限循环语句,那这个线程将无法自动停止。

    Python线程退出条件有以下几种:

    1、线程内的函数语句执行完毕,线程自动结束

    2、线程内的函数抛出未处理的异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import threading
    import time
     
    def worker(arg):
        while True:
            time.sleep(1)
            print("I'm working {}".format(arg))
        print("Fineshed")
     
    t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")
    t.start()
    运行结果:
    I'm working <_MainThread(MainThread, stopped 2468)>
    I'm working <_MainThread(MainThread, stopped 2468)>
    I'm working <_MainThread(MainThread, stopped 2468)>
    ...

      上面例子中,线程启动后,将一直循环下去,线程不会自动退出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import threading
    import time
     
    def worker(arg):
        count = 0
        while True:
            if count > 5:
                raise RuntimeError(count)
            time.sleep(1)
            print("I'm working {}".format(arg))
            count += 1
        print("Fineshed")
     
    t = threading.Thread(target=worker,args=(threading.enumerate(),))
    t.start()
     
    print("====end===")
     
    运行结果:
    ====end===
    I'm working [<_MainThread(MainThread, stopped 10992)>]
    I'm working [<_MainThread(MainThread, stopped 10992)>]
    I'm working [<_MainThread(MainThread, stopped 10992)>]
    I'm working [<_MainThread(MainThread, stopped 10992)>]
    I'm working [<_MainThread(MainThread, stopped 10992)>]
    I'm working [<_MainThread(MainThread, stopped 10992)>]
    Exception in thread Thread-1:
    Traceback (most recent call last):
      File "C:/python/test.py", line 8, in worker
        raise RuntimeError(count)
    RuntimeError: 6

      上面例子中,演示了触发异常自动退出线程。但最先打印的是主程序的"===end==="语句,是因为在程序中,主线程启动一个线程后,不会等待子线程执行完毕,就继续执行了后续语句,在执行完主线程语句后,发现还有子线程没有结束,于是等待子线程执行结束,子线程在运行时抛出了未处理的异常,最终子线程结束,主线程也随之结束。这里需要了解daemon线程和non-daemon线程,稍后就会介绍。

    threading属性:

    threading.current_thread()   返回当前线程对象
    threading.main_thread() 返回主线程对象
    threading.active_count() 返回处于Active状态的线程个数
    threading.enumerate() 返回所有存活的线程的列表,不包括已经终止的线程和未启动的线程
    threading.get_ident() 返回当前线程的ID,非0整数

    举例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import threading
    import time
     
    def showthreadinfo():
        print("current thread = {}".format(threading.current_thread()))
        print("main thread  = {}".format(threading.main_thread()))
        print("active thread count = {}".format(threading.active_count()))
        print("active thread list = {}".format(threading.enumerate()))
        print("thread id = {}".format(threading.get_ident()))
        print("~~~~~~~~~~~~~")
     
    def add(x,y):
        time.sleep(1)
        showthreadinfo() #子线程中调用
        print(x+y)
     
    showthreadinfo() #主线程中调用
    time.sleep(1)
     
    t = threading.Thread(target=add,args=(4,5))
    t.start()
     
    print("====end===")
     
    运行结果:
    current thread = <_MainThread(MainThread, started 192)>
    main thread  = <_MainThread(MainThread, started 192)>
    active thread count = 1
    active thread list = [<_MainThread(MainThread, started 192)>]
    thread id = 192
    ~~~~~~~~~~~~~
    ====end===
    current thread = <Thread(Thread-1, started 8424)>
    main thread  = <_MainThread(MainThread, stopped 192)>
    active thread count = 2
    active thread list = [<_MainThread(MainThread, stopped 192)>, <Thread(Thread-1, started 8424)>]
    thread id = 8424
    ~~~~~~~~~~~~~
    9

      上面例子中,在主线程中只能看到存活的只有自己,因为子线程还没有启动,且它的父线程就是它自己。子线程启动时,它的名字为Thread-1,这个名字是解释器自动命名的,如果定义线程对象时添加了name="threadName",则这里显示的就是threadName;同时,子线程的父线程就是主线程,也就是说谁启动的线程谁就是它的父线程;子线程能看到的存活线程有父线程和自身。

    Thread实例的属性:

    threading.current_thread().name        线程名,只是一个标识符,可以使用getName()、setName()获取和运行时重命名。
    threading.current_thread().ident 线程ID,非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。此ID可以重复使用
    threading.current_thread().is_alive() 返回线程是否存活,布尔值,True或False。

    举例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import threading
    import time
     
    def worker():
        count = 1
        while True:
            if count >= 6:
                break
            time.sleep(1)
            count += 1
            print("thread name = {}".format(threading.current_thread().name))
     
    t = threading.Thread(target=worker,name="MyThread")
    t.start()
     
    while True:
        time.sleep(1.1)
        if t.is_alive():
            print("{} {} alive".format(t.name,t.ident))
        else:
            print("{} {} alive".format(t.name, t.ident))
            t.start()
     
    print("====end===")
     
    运行结果:
    thread name = MyThread
    MyThread 9400 alive
    thread name = MyThread
    MyThread 9400 alive
    thread name = MyThread
    MyThread 9400 alive
    thread name = MyThread
    MyThread 9400 alive
    thread name = MyThread
    MyThread 9400 alive
    Traceback (most recent call last):
      File "C:/python/test.py", line 22, in <module>
        t.start()
        raise RuntimeError("threads can only be started once")
    RuntimeError: threads can only be started once

      从上面例子中可以看到子线程存活时的名字和线程ID,但在线程退出后,尝试再次启动线程时,抛出RuntimeError异常,表明线程对象在定义后只能启动一次。

     举例 getName()和setName():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    import threading
    import time
     
    def add(x,y):
        for _ in range(5):
            time.sleep(1)
            print("x+y={}".format(x+y))
     
    t = threading.Thread(target=add,name="MyThread",args=(6,7))
    t.start()
     
    while True:
        time.sleep(1)
        if t.is_alive():
            print("{} {} alive".format(t.name,t.ident))
            print("Thread name",t.getName())
            t.setName("MyThreadTwo")
        else:
            print("{} {} alive".format(t.name, t.ident))
            print("Thread abort....")
            break
            # t.start()
     
    print("====end===")
     
    运行结果:
    MyThread 2564 alive
    Thread name MyThread
    x+y=13
    MyThreadTwo 2564 alive
    Thread name MyThreadTwo
    x+y=13
    MyThreadTwo 2564 alive
    Thread name MyThreadTwo
    x+y=13
    MyThreadTwo 2564 alive
    Thread name MyThreadTwo
    x+y=13
    MyThreadTwo 2564 alive
    Thread name MyThreadTwo
    x+y=13
    MyThreadTwo 2564 alive
    Thread abort....
    ====end===

      上面例子演示了在运行时获取线程名和重命名线程名。

    线程的start()和run()方法:

    start():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import threading
    import time
     
    def add(x,y):
        for _ in range(5):
            time.sleep(0.5)
            print("x+y={}".format(x+y))
     
    class MyThread(threading.Thread):
        def start(self):
            print('start~~~~~~~~~~')
            super().start()
     
        def run(self):
            print('run~~~~~~~~~~~~')
            super().run()  #调用父类的start()和run()方法
     
     
    t = MyThread(target=add,name="MyThread",args=(6,7))
    t.start()
    # t.run()
    print("====end===")
     
    运行结果:
    start~~~~~~~~~~
    run~~~~~~~~~~~~
    ====end===
    x+y=13
    x+y=13
    x+y=13
    x+y=13
    x+y=13

      从上面的例子中,可以看出start()方法会先运行start()方法,再运行run()方法。

    跟进一下start() 方法源码中的调用过程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    1def start(self):
        _start_new_thread(self._bootstrap, ())
        ....
     
    2、_start_new_thread = _thread.start_new_thread
     
    3def start_new_thread(function, args, kwargs=None):
        pass
     
    4def _bootstrap(self):
        self._bootstrap_inner()
     
    5def _bootstrap_inner(self):
        ....
        try:
            self.run()#最终start()方法调用了run()方法
        except SystemExit:
            pass

      从上面跟踪源码的过程大概了解了start()方法如何调用到了run()方法。

    run()方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import threading
    import time
     
    def add(x,y):
        for _ in range(5):
            time.sleep(0.5)
            print("x+y={}".format(x+y))
     
    class MyThread(threading.Thread):
        def start(self):
            print('start~~~~~~~~~~')
            super().start()
     
        def run(self):
            print('run~~~~~~~~~~~~')
            super().run()  #调用父类的start()和run()方法
     
     
    t = MyThread(target=add,name="MyThread",args=(6,7))
    # t.start()
    t.run()
    print("====end===")
     
    运行结果:
    run~~~~~~~~~~~~
    x+y=13
    x+y=13
    x+y=13
    x+y=13
    x+y=13
    ====end===

      上面例子中,运行线程的run()方法只能调用到run()方法。

    跟踪一下run() 方法在源码中的调用过程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1def __init__(self, group=None, target=None, name=None,
                     args=(), kwargs=None, *, daemon=None):
        self._target = target
        self._args = args
        self._kwargs = kwargs
        ....
     
    2def run(self):
        if self._target:
            self._target(*self._args, **self._kwargs)
        ....

      可以看出,_target是我们传入的目标函数,run()方法其实就类似一个装饰器,最终还是将_args 和_kwargs 参数传入目标函数运行,返回结果。

    start() --> run() --> _target()

    run() --> _target()

    上面两个例子简单介绍了start()方法和run()方法的调用,下一篇文章再详细看一下它们到底有什么区别。

    总结:

    本文主要介绍了: Thread类、线程启动、线程的传参、线程退出、threading属性、Thread实例的属性、举例getName()和setName()、线程的start()和run()方法

  • 相关阅读:
    RHEL7挂载ISO做本地yum
    服务器虚拟化架构突出优势
    国内公共DNS
    世界上最小的发行版之一Tiny Core
    VMware v12.1.1 专业版以及永久密钥
    Webbench网站压力测试
    Web.xml配置详解之context-param
    SpringMvc自动代理
    SpringMvc aop before
    SpringMvc 面向切面1
  • 原文地址:https://www.cnblogs.com/kaishirenshi/p/9718004.html
Copyright © 2011-2022 走看看