zoukankan      html  css  js  c++  java
  • Python并发编程-线程

           Python并发编程-线程

                           作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.进程和线程

    1>.什么是进程

      进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
    
      在早期面向进程设计的计算机结构中,进程是程序的基本执行实体。
    
      在当代面向线程设计的计算机结构中,进程是线程的容器。
    
      程序是指令、数据及其组织形式的描述,进程是程序的实体。

    2>.进程和程序的关系

      程序是源代码编译后的文件,而在这些文件存放在磁盘上。

      当程序被操作系统加载到内存中,就是进程,进程中存放着指令和数据(资源),它也是线程的容器。   Linux进程有父进程,子进程,windows的进程是平等关系。

    3>.什么是线程

      线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
    
      线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。
      同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread
    -local storage)。
      一个进程可以有很多线程,每条线程并行执行不同的任务。

    4>.进程,线程的理解

      现代操作系统提出进程的概念,每一个进程都认为自己独占所有的计算机硬件资源。
    
      进程就是独立的王国,进程间不可以随便的共享数据。
    
      线程就是省份,同一个进程内的线程可以共享进程的资源,每一个线程拥有自己独立的堆栈信息。
    
      为了方便我们队线程和进程的理解,我们来画2组图,方便我们对python中的线程和进程的理解,其他语言线程与进程的关系如下图:

      python语言线程与进程的关系如下图:

    如上图所示,线程进程介绍如下:
      (1)计算机工作最小单元是线程;   (2)启动一个python可执行文件->应用程序->最少要有一个进程>最少要一有一个线程   (3)应用场景:     IO密集型:线程     计算密集型:进程   (4)GIL,全局解释器所。     功能:保证同一个进程中只有一个线程同时被调用。(以上三点是所有语言的共同之处,只是CPYTHON多了一个GIL规则而已。)

    5>.线程的状态

    就绪(ready):
      线程能够运行,但在等待被调度。可能线程刚刚创建启动,或刚刚从阻塞中恢复,或者被其它线程抢占。 运行(running):
      线程正在运行。 阻塞(blocked):
      线程等待外部时间发生而无法运行,如I/O操作。 终止(terminated):
      线程完成或退出或被取消。

    6>.python中的线程和进程

      运行程序会启动一个解释器进程,线程共享一个解释器进程。
    
      Python的线程开发使用标准库thread。
    
      进程靠线程执行代码,至少有一个主线程,其它线程是工作线程。
    
      主线程是第一个启动的线程。
    
      父线程:如果线程A中启动了一个线程B,A就是线程B的父线程。
    
      子线程:B就是A的子线程。

      python的线程没有优先级,没有线程组的概念,也不能被销毁,停止,挂起,那也就没有恢复,中断了。

    二.Thread类

    1>.Thread类的构造方法

    group:
        为将来实现ThreadGroup类时的扩展保留。
    
    target:
        是run()方法要调用的可调用对象。默认为“无”,表示不调用任何内容。即线程调用的对象,就是目标函数。
    
    name:
        是线程名。默认情况下,一个唯一的名称由“Thread-N”构成,其中N是一个小的十进制数。
    
    args:
        是目标调用函数的参数元组。默认为()。
    
    kwargs:
        是目标调用的关键字参数字典。默认为{}。

    2>.线程启动

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import time
     8 
     9 def worker(size):
    10     count = 0
    11     while True:
    12         if count > size:
    13                 """
    14                     Python没有提供线程退出的方法,线程在下面的情况时退出:
    15                         1>.线程函数中语句执行完毕
    16                         2>.线程函数中抛出未处理的异常
    17                 """
    18                 # raise RuntimeError(count)     #抛异常
    19                 # return                        #函数返回
    20                 break
    21         time.sleep(1)
    22         count += 1
    23         print("I'm working")
    24     print("Finished")
    25 
    26 """
    27     通过threading.Thread创建一个线程对象:
    28         使用target指定目标函数,
    29         使用name指定线程名称,
    30         使用args对目标函数进行传参,线程传参和函数传参没有什么区别,本质上就是函数传参。
    31     但是线程没有启动,需要调用start方法。
    32 """
    33 t = threading.Thread(target=worker,name="worker",args=(5,))          #创建一个线程对象
    34 t.start()                                                            #启动线程
    35 
    36 print("=== main end ===")
    === main end ===
    I'm working
    I'm working
    I'm working
    I'm working
    I'm working
    I'm working
    Finished
    以上代码执行结果戳这里

    3>.threading的属性和方法

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import time
     8 
     9 def worker(size):
    10     count = 0
    11     while True:
    12         if count > size:
    13                 break
    14         time.sleep(1)
    15         count += 1
    16         print("I'm working")
    17     print("Finished")
    18 
    19 def show_read_info():
    20     """
    21     threading的属性和方法:
    22         current_thread():
    23             返回当前线程对象。
    24         main_thread():
    25             返回主线程对象。
    26          active_count():
    27             当前处于alive状态的线程个数,返回的值还包括主线程哟。
    28         enumerate():
    29             返回所有活着的线程列表,不包括已经终止的线程和未开始的线程,返回的值还包括主线程哟。
    30         get_ident():
    31             返回当前线程的ID,非0的整数。
    32     """
    33     print("current thread = {}
    main thread = {}
    active count = {}
    enumberate list ={}
    Thread ID = {}".format(
    34         threading.current_thread(),
    35         threading.main_thread(),
    36         threading.active_count(),
    37         threading.enumerate(),
    38         threading.get_ident()
    39     ))
    40 
    41 t = threading.Thread(target=worker,name="worker",args=(5,))          #创建一个线程对象
    42 
    43 show_read_info()
    44 time.sleep(1)
    45 t.start()                                                            #启动线程
    46 show_read_info()
    47 print("=== main end ===")
    current thread = <_MainThread(MainThread, started 16776)>
    main thread = <_MainThread(MainThread, started 16776)>
    active count = 1
    enumberate list =[<_MainThread(MainThread, started 16776)>]
    Thread ID = 16776
    current thread = <_MainThread(MainThread, started 16776)>
    main thread = <_MainThread(MainThread, started 16776)>
    active count = 2
    enumberate list =[<_MainThread(MainThread, started 16776)>, <Thread(worker, started 4048)>]
    Thread ID = 16776
    === main end ===
    I'm working
    I'm working
    I'm working
    I'm working
    I'm working
    I'm working
    Finished
    以上代码执行结果戳这里

    4>.Thread实例的属性和方法

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import time
     8 
     9 def worker(size):
    10     count = 0
    11     while True:
    12         if count > size:
    13                 break
    14         time.sleep(1)
    15         count += 1
    16         print("I'm working")
    17     print("Finished")
    18 
    19 class MyThread(threading.Thread):
    20     def start(self):
    21         time.sleep(1)
    22         print("{0} start {0}".format("*" * 15))
    23         super().start()
    24 
    25     def run(self):
    26         time.sleep(1)
    27         print("{0} run {0}".format("*" * 15))
    28 
    29 t = MyThread(target=worker,name="worker",args=(5,))          #创建一个线程对象
    30 """
    31     Thread实例的属性和方法
    32         name:
    33             只是一个名字,只是个标识,名称可以重名。通过getName(),setName()可以获取,设置这个名字。
    34         ident:
    35             线程ID必须唯一,它是一个非0整数,线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问,此ID可以重复使用。
    36         is_alive():
    37             返回线程是否活着。
    38         start():
    39             启动一个新的线程并调用run方法。start方法只能调用1次(设置_startd属性实现)
    40         run():
    41             并不启动新的线程,就是在主线程调用了一个普通函数而已。run方法可以多次调用。
    42 """
    43 
    44 print("{} {} {}".format(t.name, t.ident, "alive" if t.is_alive() else "dead"))
    45 print(threading.enumerate())
    46 t.start()
    47 # t.run()
    48 # t.run()
    49 # t.run()
    50 print("{} {} {}".format(t.name, t.ident, "alive" if t.is_alive() else "dead"))
    51 print(threading.enumerate())
    52 time.sleep(3)
    53 
    54 print("{} {} {}".format(t.name, t.ident, "alive" if t.is_alive() else "dead"))
    55 print(threading.enumerate())
    worker None dead
    [<_MainThread(MainThread, started 15888)>]
    *************** start ***************
    worker 16460 alive
    [<_MainThread(MainThread, started 15888)>, <MyThread(worker, started 16460)>]
    *************** run ***************
    worker 16460 dead
    [<_MainThread(MainThread, started 15888)>]
    
    Process finished with exit code 0
    以上代码执行结果戳这里

     

    三.线程安全

    1>.什么是线程安全

      线程执行一段代码,不会产生不确定的结果,那这段代码就是线程安全的。

    2>.打印线程不安全案例

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 
     8 def worker():
     9     for i in range(100):
    10         print("{} is running.".format(threading.current_thread().name))     #print函数不是线程安全函数。
    11 
    12 
    13 for x in range(1,30):           #可以增加线程数,使用pycharm + python3.7.4版本观察输出结果,发现print函数被打断了,被线程切换打断了,在这说明print函数是线程不安全的。
    14     name = "worker {}".format(x)
    15     t = threading.Thread(name=name,target=worker)
    16     t.start()

    3>.解决案例中打印线程不安全方案一(不让print打印换行)

    #!/usr/bin/env python
    #_*_conding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie
    
    import threading
    
    def worker():
        for i in range(100):
            print("{} is running.
    ".format(threading.current_thread().name),end="")    #字符串是不可变类型,它可以作为一个整体不可分割输出。end=""就不在让print输出换行了。
    
    
    for x in range(1,30):            
        name = "worker {}".format(x)
        t = threading.Thread(name=name,target=worker)
        t.start()

    4>.解决案例中打印线程不安全方案二(使用logging模块)

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import logging
     8 
     9 def worker():
    10     for i in range(100):
    11         logging.warning("{} is running.".format(threading.current_thread().name))
    12 
    13 
    14 for x in range(1,30):
    15     name = "worker {}".format(x)
    16     t = threading.Thread(name=name,target=worker)
    17     t.start()

    四.daemon线程和non-daemon线程

    1>.demon与non-daemon线程案例

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import logging
     8 import time
     9 
    10 def worker(timeout):
    11     time.sleep(timeout)
    12     for i in range(10):
    13         logging.warning("{} is running.".format(threading.current_thread().name))
    14 
    15 
    16 """
    17 daemon线程:
    18     daemon属性:表示线程是否时daemon线程,这个值必须在start()之前设置,否则引发RuntimeError异常。
    19     isDaemon属性:是否是daemon线程。
    20     setDaemon设置威威daemon线程,必须咋子start方法之前设置。
    21     
    22 总结:
    23     线程具有一个daemon属性,可以手动设置为True或False,也可以不设置,则取默认值None。
    24     如果不设置daemon,就取当前线程的daemon来设置它。
    25     主线程是non-daemon线程,即daemon=False。
    26     从主线程创建的所有线程的不设置daemon属性,则默认都是daemon=Flase,也就是non-daemon线程。
    27     Python程序在没有或者的non-deamon线程运行时,程序退出,也就是除主线程之外剩下的只能都是daemon线程,主线程才能退出,否则主线程就只能等待。
    28 """
    29 t1 = threading.Thread(target=worker,name="t1",args=(10,),daemon=True)
    30 t2 = threading.Thread(target=worker,name="t2",args=(5,),daemon=False)
    31 t1.start()
    32 t2.start()
    33 
    34 
    35 
    36 print("Main Thread Exits!")
    Main Thread Exits!
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    WARNING:root:t2 is running.
    以上代码执行结果戳这里

    2>.join方法和demon线程

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import logging
     8 import time
     9 
    10 def worker():
    11     for i in range(10):
    12         time.sleep(1)
    13         logging.warning("{} is running.".format(threading.current_thread().name))
    14 
    15 
    16 
    17 t1 = threading.Thread(target=worker,name="t1",daemon=True)
    18 t1.start()
    19 
    20 """
    21 join方法是线程的标准方法之一,相关总结如下:
    22     一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用者用线程终止
    23     一个线程可以被join多次
    24     join(timeout=None),timeout参数指定调用者等待多久,没有设置超时,就一直等到被调用线程结束
    25     调用谁的join方法,就是join谁,就要等谁。
    26 """
    27 # t1.join()
    28 t1.join(5)
    29 
    30 
    31 print("Main Thread Exits!")
    WARNING:root:t1 is running.
    WARNING:root:t1 is running.
    WARNING:root:t1 is running.
    WARNING:root:t1 is running.
    Main Thread Exits!
    以上代码执行结果戳这里

    3>.daemon线程应用场景

    主要应用场景有:
        (1)后台任务
            如发送心跳包,监控,这种场景最多。
        (2)主线程工作才有用的线程
          如主线程中维护着公共的资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适。 (
    3)随时可以被终止的线程
          如果主线程退出,想所有其它工作线程一起退出,就使用daemon=True来创建线程。比如,开启一个线程定时判断WEB服务是否正常工作,主线程退出,工作线程也没有必要存在了,应该随着主线程退出一起退出。这种daemon线程一旦创建,就可以忘记它了,只用关心主线程什么时候退出就行了。

    五.threading.local类

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import logging
     8 import time
     9 
    10 """
    11 threading.local本质:
    12     运行时,threading.local实例处在不同的线程中,就从大字典中找到当前线程相关键值对中的字典,覆盖threading.local实例的"__dict__"。
    13     这样就可以在不同的线程中,安全地使用线程独有的数据,做到了线程间数据隔离,如同本地变量一样安全。
    14 """
    15 global_data = threading.local()
    16 
    17 def worker():
    18     global_data.x = 0
    19     for i in range(1,10000):
    20         time.sleep(0.0000001)
    21         global_data.x += 1
    22     print(threading.current_thread(),global_data.x)
    23 
    24 
    25 for i in range(10):
    26     threading.Thread(target=worker).start()
    27 
    28 print("Main Thread Exits!")
    Main Thread Exits!
    <Thread(Thread-1, started 1080)> 9999
    <Thread(Thread-7, started 16592)> 9999
    <Thread(Thread-10, started 10780)> 9999
    <Thread(Thread-2, started 4352)> 9999
    <Thread(Thread-4, started 14460)> 9999
    <Thread(Thread-3, started 11792)> 9999
    <Thread(Thread-9, started 1620)> 9999
    <Thread(Thread-8, started 14884)> 9999
    <Thread(Thread-5, started 17152)> 9999
    <Thread(Thread-6, started 5924)> 9999
    以上代码执行结果戳这里
  • 相关阅读:
    APP性能之终端兼容优化分享
    Java反射机制
    语音编码的WAVE文件头格式剖析
    【原创】ASP.NET MVC3开发中遇到问题以及解决方法
    linux常用命令(基础)
    vue中粘贴板clipboard的使用方法
    解决部署zabbix中zabbixagent的状态为灰色现象
    IAR Embedded Workbench for ARM: Porting code from V4 to V5 ( for stm32)
    MSDN帮助文档 "无法显示该网页" 的问题解决方案(转)
    二叉排序树求每个结点平衡因子程序
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11875147.html
Copyright © 2011-2022 走看看