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

    1、并发和并行的区别

      并行:parallel

        同一时刻上,有多件互不干扰的事要做。

      并发:concurrency

        同一时间内,多少事要做。

      补充:

    buffer(缓冲)是为了提高内存和硬盘或其他I/0设备之间的数据交换的速度而设计的。 
    cache(缓存)是为了提高cpu和内存之间的数据交换速度而设计。 

    2、并发的解决

      什么是高并发:同一个时间段,发起大量的数据请求

      2.1、队列、缓冲区:

        使用队列就是,其实就是一个缓冲地带,即缓冲区,当然根据优先级别,设置不同的队列,优先级高的的先处理。例如queue模块中的类Queue , LifoQueue, PriorityQueue

      2.2、争抢型:网站一般不会选择抢占处理并发,否则有些用户总觉得反应慢。

        资源争抢型,当争抢到资源后,就不会为其他请求提供服务,这就是:锁机制 其他请求只能等待。

        但是这样并不好,因为有可能有的请求一直抢不到资源。

      2.3、预处理:基于lru原则

        一种提前加载用户所需数据的思路,即预处理思想,也就是缓存思想

        常用的是字典,如果数据量大的时候,使用内存数据库,即Redis 或者memcached。

      2.4、并行:

        日常可以通过购买更多的服务器,或多开进程,线程实现并行处理,来解决并发问题。

        注意这些都是水平扩展 思想

        但是 这样成本上升。

        :

        每个cpu称为一路,一个cpu有多核,如果 单cpu单核 是无法并行,看起来是像 并行,但并不是,只是轮询。

      2.5、提速:

        提高单个cpu的性能,或者单个服务器安装更多的cpu,或者提高程序的效率。

        垂直扩展思想

      2.6、消息中间件:

        即系统之外的队列,常见的消息中间件:RabbitMQ, ActvieMQ(Apache),RocketMQ( 阿里Apache), kafka(Apavhe)。

        通过消息中间件,成为分布式系统,对生产者和消费者(程序之间), 解耦同时做缓冲,将数据平滑起来。

      2.7、多地区:多地服务器

    3、进程和线程

      在实现了线程的操作系统中(并不是所有的os都实现了 线程),线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位,一个程序的执行实例就是一个进程。

      进程(process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

      进程和程序的区别:

        程序是源代码编译后的文件,而这些文件存放在磁盘上,当程序被操作系统加载到内存中,就是进程,进程中存放着指令和数据(资源),它也是线程的容器。

      Linux是分父进程和子进程,Windows的进程时平等关系。

      线程,有时 被称为轻量级进程( Lightweight Process ,  LWP),是程序执行流的最小单元。

      一个标准的线程由线程ID ,当前指令指针(PC),寄存器集合和堆栈组成。

      在许多系统中,创建一个线程比创建一个进程块 10 --- 100 倍,但这并不是说线程越多越好。

      进程和线程的理解

        现代操作系统提出进程的概念,没一个进程都认为自己独自占有所有的计算机硬件资源,

        进程就是独立的王国,进程间不可以随便共享数据。

        线程可以共享进程的资源,但是线程之间也不是可以随便共享数据。

        每一个线程拥有自己独立的堆栈。不一定都实现堆,但是一定实现栈。

    4、线程的状态:

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

        

        注:针对单cpu单核:cpu一次只能调度一个线程,要不抢占,要不优先,要不排队。

          单cpu单核,轮询对线程调度,每个线程调度时间片由于优先级也有不同。

    5、Python中的进程和线程。

      进程会启动一个解释器进程,线程共享一个解释器进程。 

    6、Python的线程开发:

      Python的线程开发使用标准库threading 

    7、Thread类 

    Thread原码:
    def __init__(self, group=None, target=None, name=None,
                  args=(), kwargs=None, *, daemon=None):

      target:线程调用的对象,就是目标函数

      name:为线程起一个名字

      args:为目标函数传递实参,元组(注意一个元素的时候,逗号)

      kwarg:为目标函数关键字传参,字典

    8、线程启动:

     1 import  threading
     2 
     3 # 最简单的线程程序
     4 def work():
     5     print('working')
     6     print('finished')
     7 
     8 t = threading.Thread(target=work, name='work1')
     9 
    10 t.start()

      线程之所以执行函数是因为线程中就是执行代码的,而最简单的封装就是函数,所以还是函数调用。

      函数执行完,线程也就退出了。

      如果不让线程退出,或者让线程一直工作。通过死循环。 

     1 import  threading
     2 import time
     3 
     4 def work():
     5     while True:
     6         time.sleep(1)
     7         print('working')
     8     print('finished')
     9 
    10 t = threading.Thread(target=work, name='work1')
    11 
    12 t.start()

    9、线程退出:

      Python没有提供线程退出的方法,线程在下面的情况下退出:

      1、线程函数内语句执行完毕

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

     1 import  threading
     2 import time
     3 
     4 def work():
     5     count = 0
     6     while True:
     7         if count > 5:
     8             break
     9             # raise Exception('count')
    10             # return 也是可以的
    11         time.sleep(1)
    12         print('working')
    13         count += 1
    14 
    15 t = threading.Thread(target=work, name='work1')
    16 t.start()
    17 print('--------------------')
    18 print('======= end  =======')
    1 --------------------
    2 ======= end  =======
    3 working
    4 working
    5 working
    6 working
    7 working
    8 working
    结果

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

    10、线程的传参

     1 import  threading
     2 import time
     3 
     4 def add(x, y):
     5     print('{} + {} = {}'.format(x, y, x + y), threading.current_thread().ident)
     6 
     7 t1 = threading.Thread(target=add, name='add', args=(4, 5))
     8 t1.start()
     9 
    10 time.sleep(2)
    11 
    12 t2 = threading.Thread(target=add, name='add', args=(5,), kwargs={'y':4})
    13 t2.start()
    14 
    15 time.sleep(2)
    16 
    17 t3 = threading.Thread(target=add, name='add', kwargs={'x':4, 'y':5})
    18 t3.start()
    1 4 + 5 = 9 8604
    2 5 + 4 = 9 9836
    3 4 + 5 = 9 10932
    结果

      :线程传参和函数传参没有什么区别,本质上就是函数传参。

      threading的属性和方法:

    名称 含义
    current_thread() 返回当前线程对象
    main_thread() 返回主线程对象
    active_count() 当前处于alive状态的线程个数
    enumerate() 返回所有活着的线程列表,不包括已经终止的线程和未开始的线程
    get_ident() 返回当前线程的ID ,非0 的整数

        active_cont, enumerate方法返回的值还包括主线程。

        测试:

     1 import  threading
     2 import time
     3 
     4 def showthreadinfo():
     5     print('currenttherad = {}'.format(threading.current_thread()))
     6     print('main thread = {}'.format(threading.main_thread()))
     7     print('active count = {}'.format(threading.active_count()))
     8     print('enmuerate = {}'.format(threading.enumerate()))
     9 
    10 def work():
    11     count = 0
    12     showthreadinfo()
    13     while True:
    14         if count > 5:
    15             break
    16         time.sleep(1)
    17         count += 1
    18         print('working')
    19 
    20 t = threading.Thread(target=work, name='work1')
    21 t.start()
    22 
    23 print('============ end ==========')

        结果:可以看到有并行的影子,虽然这是假并行!!!

      

    11、Thread实例的属性和方法

      name:只是一个名字,只是个标记,名称可以重名。getName(), setName() 获取,设置这个名词

      ident:线程ID, 非0整数,线程启动后才会有ID,否则为None,线程退出,此 ID 依旧可以访问,此ID 可以重复使用。

      is_alive(): 返回线程是否活着。

      注: 线程的name这是一个名称,可以重复,ID 必须唯一,但是在线程退出后,可以再利用。

      测试:

     1 import  threading
     2 import time
     3 
     4 def work():
     5     count = 0
     6     while True:
     7         if count > 5:
     8             break
     9         time.sleep(1)
    10         count += 1
    11         print(threading.current_thread().name)
    12 
    13 
    14 t = threading.Thread(target=work, name='work1')
    15 t.start()
    16 
    17 while True:
    18     time.sleep(1)
    19     if t.is_alive():
    20         print('{}{} alive'.format(t.name, t.ident))
    21     else:
    22         print('{}{} dead'.format(t.name, t.ident))
    23         t.start()

      结果:结果很明确, 线程对象,只能执行一次start,尽管对象还存在。

      

      start(): 启动线程,每一个线程必须且只能执行该方法 一次

      run(); 运行线程函数

      测试  start():

     1 import  threading
     2 import time
     3 
     4 def work():
     5     count = 0
     6     while True:
     7         if count > 5:
     8             break
     9         time.sleep(1)
    10         count += 1
    11         print('working------------')
    12 
    13 class MyThread(threading.Thread):
    14     def start(self):
    15         print('start -----------------')
    16         super().start()
    17 
    18     def run(self):
    19         print('run -------------------')
    20         super().run()
    21 
    22 
    23 t = MyThread(target=work, name='work1')
    24 t.start()
    start()

      结果:  

    1 start -----------------
    2 run -------------------
    3 working------------
    4 working------------
    5 working------------
    6 working------------
    7 working------------
    8 working------------
    结果

      测试 run():

     1 import  threading
     2 import time
     3 
     4 def work():
     5     count = 0
     6     while True:
     7         if count > 5:
     8             break
     9         time.sleep(1)
    10         count += 1
    11         print('working------------')
    12 
    13 class MyThread(threading.Thread):
    14     def start(self):
    15         print('start -----------------')
    16         super().start()
    17 
    18     def run(self):
    19         print('run -------------------')
    20         super().run()
    21 
    22 
    23 t = MyThread(target=work, name='work1')
    24 t.run()
    run()

      结果:

    1 run -------------------
    2 working------------
    3 working------------
    4 working------------
    5 working------------
    6 working------------
    7 working------------
    结果

      总结:start() 方法会调用run() 方法,而run() 方法可以运行函数。

     1 ## run()
     2 import  threading
     3 import time
     4 
     5 def work():
     6     count = 0
     7     while True:
     8         if count > 5:
     9             break
    10         time.sleep(1)
    11         count += 1
    12         print('working------------')
    13         print(threading.current_thread().name)
    14 
    15 class MyThread(threading.Thread):
    16     def start(self):
    17         print('start -----------------')
    18         super().start()
    19 
    20     def run(self):
    21         print('run -------------------')
    22         super().run()
    23 
    24 
    25 t = MyThread(target=work, name='work1')
    26 t.run()
    27 
    28 ## start()
    29 import  threading
    30 import time
    31 
    32 def work():
    33     count = 0
    34     while True:
    35         if count > 5:
    36             break
    37         time.sleep(1)
    38         count += 1
    39         print('working------------')
    40         print(threading.current_thread().name)
    41 
    42 class MyThread(threading.Thread):
    43     def start(self):
    44         print('start -----------------')
    45         super().start()
    46 
    47     def run(self):
    48         print('run -------------------')
    49         super().run()
    50 
    51 
    52 t = MyThread(target=work, name='work1')
    53 t.start()
    比较start和run

      结果:

     1 ## start
     2 start -----------------
     3 run -------------------
     4 working------------
     5 work1
     6 working------------
     7 work1
     8 working------------
     9 work1
    10 working------------
    11 work1
    12 working------------
    13 work1
    14 working------------
    15 work1
    16 
    17 ## run
    18 run -------------------
    19 working------------
    20 MainThread
    21 working------------
    22 MainThread
    23 working------------
    24 MainThread
    25 working------------
    26 MainThread
    27 working------------
    28 MainThread
    29 working------------
    30 MainThread
    结果

      使用start方法启动线程,启动了一个新的线程,名字叫work1 运行,但是使用run方法的,并没有启动新的线程,就是在主线程中调用了一个普通的函数而已。

      因此,启动线程使用start方法,才能启动多个线程。

      run就是单线程的,start是多线程的。

      线程适可而止,不能太多,否则时间都浪费到切换上了。而且线程是共享资源的,100个线程和100000个线程对资源的获取是不同的。

        start只能一次,其次ID 在线程消亡后 还会留给之前的线程
    
        run 只是运行线程函数,不会创建线程,依旧还是主线程,在主线程中运行线程函数,
            单线程 是 顺序执行,从上往下执行。
    
        start 创建子线程,并通过run执行线程函数。

     12、多线程:

      顾明思议,多线程, 一个进程中如果有多个线程,就是多线程,实现一种并发。

      测试: start

     1 import threading
     2 import time
     3 
     4 def work():
     5     count = 0
     6     while True:
     7         if count > 5:
     8             break
     9         time.sleep(0.5)
    10         count += 1
    11         print('worker running')
    12         print(threading.current_thread().name, threading.current_thread().ident)
    13 class MyThread(threading.Thread):
    14     def start(self):
    15         print('start -----')
    16         super().start()
    17 
    18     def run(self):
    19         print('run -------')
    20         super().run()
    21 
    22 t1 = threading.Thread(name='worker1', target=work)
    23 t2 = threading.Thread(name='worker2', target=work)
    24 
    25 t1.start()
    26 t2.start()
    start

      结果:

     1 worker runningworker running
     2 worker2
     3  632
     4 worker1 7456
     5 worker running
     6 worker1 7456
     7 worker running
     8 worker2 632
     9 worker running
    10 worker1 7456
    11 worker running
    12 worker2 632
    13 worker runningworker running
    14 worker2 632
    15 
    16 worker1 7456
    17 worker running
    18 worker2 632
    19 worker running
    20 worker1 7456
    21 worker running
    22 worker2 632
    23 worker running
    24 worker1 7456
    View Code

      测试: run

     1 import threading
     2 import time
     3 
     4 def work():
     5     count = 0
     6     while True:
     7         if count > 5:
     8             break
     9         time.sleep(0.5)
    10         count += 1
    11         print('worker running')
    12         print(threading.current_thread().name, threading.current_thread().ident)
    13 class MyThread(threading.Thread):
    14     def start(self):
    15         print('start -----')
    16         super().start()
    17 
    18     def run(self):
    19         print('run -------')
    20         super().run()
    21 
    22 t1 = MyThread(name='worker1', target=work)
    23 t2 = MyThread(name='worker2', target=work)
    24 
    25 # t1.start()
    26 # t2.start()
    27 t1.run()
    28 t2.run()
    run

       结果:

     1 run -------
     2 worker running
     3 MainThread 8868
     4 worker running
     5 MainThread 8868
     6 worker running
     7 MainThread 8868
     8 worker running
     9 MainThread 8868
    10 worker running
    11 MainThread 8868
    12 worker running
    13 MainThread 8868
    14 run -------
    15 worker running
    16 MainThread 8868
    17 worker running
    18 MainThread 8868
    19 worker running
    20 MainThread 8868
    21 worker running
    22 MainThread 8868
    23 worker running
    24 MainThread 8868
    25 worker running
    26 MainThread 8868
    View Code

      可以看出:没有开新的线程, 这就是普通的函数调用,所以执行完t1.run() 然后执行t2.run() ,这里就不是多线程。

      当使用start方法启动线程后,进程内有多个活动的线程 并行的工作,就是多线程。

      一个进程中至少有一个线程,并作为程序的入口,这个线程就是主线程,一个进程至少有一个主线程。其他线程称为工作线程。

      主线程做协调管理,工作线程具体工作,线程在各自的占中调用函数,完成压栈,弹出,互相独立。

    13、线程安全:

      IPython中演示,python命令行,pycharm一般很难演示出效果:

      

      总结:

        看代码,应该是一行一行打印,但是很多字符串打印在一起,说明print函数被打断了,被线程切换打断了。

        print函数分为两步,第一步打印字符串,第二部打印换行,就在这之间发生了线程切换。

        这说明,pirnt函数是线程不安全

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

        上例中,本以为print 应该是打印文本之后紧跟着一个换行的,但并不是,所以print函数是线程不安全的

        但是发现,print打印的内容是内有打断的,所以可以不让print换行实现线程安全。

        如下测试:

     1 import threading
     2 import time
     3 
     4 def work():
     5     for x in range(100):
     6         print('{} is running
    '.format(threading.current_thread().name), end='')
     7 
     8 for x in range(1, 5):
     9     name = 'worker{}'.format(x)
    10     t = threading.Thread(name=name, target=work)
    11     t.start()
    将换行符作为内容打印

        结果: 

      1 D:python3.7python.exe E:/code_pycharm/code_test_daye/t1.py
      2 worker1 is running
      3 worker1 is running
      4 worker2 is running
      5 worker1 is running
      6 worker2 is running
      7 worker2 is running
      8 worker2 is running
      9 worker2 is running
     10 worker2 is running
     11 worker2 is running
     12 worker2 is running
     13 worker2 is running
     14 worker2 is running
     15 worker2 is running
     16 worker2 is running
     17 worker2 is running
     18 worker2 is running
     19 worker2 is running
     20 worker2 is running
     21 worker1 is running
     22 worker2 is running
     23 worker1 is running
     24 worker2 is running
     25 worker2 is running
     26 worker2 is running
     27 worker2 is running
     28 worker2 is running
     29 worker2 is running
     30 worker2 is running
     31 worker2 is running
     32 worker2 is running
     33 worker2 is running
     34 worker2 is running
     35 worker2 is running
     36 worker2 is running
     37 worker1 is running
     38 worker1 is running
     39 worker1 is running
     40 worker1 is running
     41 worker1 is running
     42 worker1 is running
     43 worker1 is running
     44 worker1 is running
     45 worker1 is running
     46 worker1 is running
     47 worker1 is running
     48 worker1 is running
     49 worker1 is running
     50 worker1 is running
     51 worker1 is running
     52 worker1 is running
     53 worker1 is running
     54 worker1 is running
     55 worker1 is running
     56 worker1 is running
     57 worker1 is running
     58 worker1 is running
     59 worker1 is running
     60 worker2 is running
     61 worker1 is running
     62 worker1 is running
     63 worker1 is running
     64 worker1 is running
     65 worker1 is running
     66 worker1 is running
     67 worker2 is running
     68 worker1 is running
     69 worker1 is running
     70 worker1 is running
     71 worker3 is running
     72 worker1 is running
     73 worker1 is running
     74 worker1 is running
     75 worker1 is running
     76 worker1 is running
     77 worker1 is running
     78 worker1 is running
     79 worker1 is running
     80 worker1 is running
     81 worker1 is running
     82 worker1 is running
     83 worker1 is running
     84 worker1 is running
     85 worker1 is running
     86 worker1 is running
     87 worker1 is running
     88 worker1 is running
     89 worker1 is running
     90 worker1 is running
     91 worker1 is running
     92 worker1 is running
     93 worker1 is running
     94 worker1 is running
     95 worker1 is running
     96 worker1 is running
     97 worker1 is running
     98 worker1 is running
     99 worker1 is running
    100 worker1 is running
    101 worker1 is running
    102 worker1 is running
    103 worker1 is running
    104 worker1 is running
    105 worker1 is running
    106 worker1 is running
    107 worker1 is running
    108 worker1 is running
    109 worker2 is running
    110 worker3 is running
    111 worker4 is running
    112 worker2 is running
    113 worker2 is running
    114 worker1 is running
    115 worker4 is running
    116 worker4 is running
    117 worker2 is running
    118 worker4 is running
    119 worker2 is running
    120 worker2 is running
    121 worker2 is running
    122 worker2 is running
    123 worker2 is running
    124 worker2 is running
    125 worker2 is running
    126 worker2 is running
    127 worker2 is running
    128 worker2 is running
    129 worker2 is running
    130 worker2 is running
    131 worker2 is running
    132 worker2 is running
    133 worker2 is running
    134 worker2 is running
    135 worker2 is running
    136 worker2 is running
    137 worker2 is running
    138 worker2 is running
    139 worker2 is running
    140 worker2 is running
    141 worker4 is running
    142 worker1 is running
    143 worker1 is running
    144 worker1 is running
    145 worker1 is running
    146 worker3 is running
    147 worker3 is running
    148 worker3 is running
    149 worker3 is running
    150 worker3 is running
    151 worker3 is running
    152 worker3 is running
    153 worker2 is running
    154 worker4 is running
    155 worker4 is running
    156 worker4 is running
    157 worker4 is running
    158 worker4 is running
    159 worker4 is running
    160 worker4 is running
    161 worker4 is running
    162 worker4 is running
    163 worker4 is running
    164 worker4 is running
    165 worker4 is running
    166 worker4 is running
    167 worker4 is running
    168 worker4 is running
    169 worker4 is running
    170 worker4 is running
    171 worker4 is running
    172 worker4 is running
    173 worker4 is running
    174 worker4 is running
    175 worker4 is running
    176 worker4 is running
    177 worker4 is running
    178 worker4 is running
    179 worker4 is running
    180 worker4 is running
    181 worker4 is running
    182 worker4 is running
    183 worker4 is running
    184 worker4 is running
    185 worker4 is running
    186 worker4 is running
    187 worker4 is running
    188 worker4 is running
    189 worker4 is running
    190 worker4 is running
    191 worker4 is running
    192 worker4 is running
    193 worker4 is running
    194 worker4 is running
    195 worker4 is running
    196 worker4 is running
    197 worker4 is running
    198 worker4 is running
    199 worker4 is running
    200 worker4 is running
    201 worker4 is running
    202 worker4 is running
    203 worker4 is running
    204 worker4 is running
    205 worker4 is running
    206 worker4 is running
    207 worker4 is running
    208 worker4 is running
    209 worker4 is running
    210 worker1 is running
    211 worker3 is running
    212 worker3 is running
    213 worker3 is running
    214 worker2 is running
    215 worker2 is running
    216 worker2 is running
    217 worker2 is running
    218 worker4 is running
    219 worker4 is running
    220 worker4 is running
    221 worker4 is running
    222 worker2 is running
    223 worker4 is running
    224 worker3 is running
    225 worker4 is running
    226 worker4 is running
    227 worker4 is running
    228 worker4 is running
    229 worker4 is running
    230 worker4 is running
    231 worker3 is running
    232 worker4 is running
    233 worker4 is running
    234 worker4 is running
    235 worker1 is running
    236 worker3 is running
    237 worker3 is running
    238 worker3 is running
    239 worker1 is running
    240 worker1 is running
    241 worker1 is running
    242 worker3 is running
    243 worker3 is running
    244 worker3 is running
    245 worker3 is running
    246 worker3 is running
    247 worker3 is running
    248 worker3 is running
    249 worker2 is running
    250 worker3 is running
    251 worker3 is running
    252 worker3 is running
    253 worker3 is running
    254 worker1 is running
    255 worker1 is running
    256 worker1 is running
    257 worker3 is running
    258 worker3 is running
    259 worker3 is running
    260 worker1 is running
    261 worker1 is running
    262 worker1 is running
    263 worker1 is running
    264 worker1 is running
    265 worker1 is running
    266 worker3 is running
    267 worker3 is running
    268 worker3 is running
    269 worker1 is running
    270 worker1 is running
    271 worker1 is running
    272 worker3 is running
    273 worker3 is running
    274 worker3 is running
    275 worker3 is running
    276 worker3 is running
    277 worker3 is running
    278 worker1 is running
    279 worker1 is running
    280 worker1 is running
    281 worker1 is running
    282 worker4 is running
    283 worker2 is running
    284 worker2 is running
    285 worker3 is running
    286 worker3 is running
    287 worker3 is running
    288 worker3 is running
    289 worker3 is running
    290 worker3 is running
    291 worker3 is running
    292 worker4 is running
    293 worker4 is running
    294 worker4 is running
    295 worker4 is running
    296 worker4 is running
    297 worker4 is running
    298 worker4 is running
    299 worker4 is running
    300 worker2 is running
    301 worker3 is running
    302 worker3 is running
    303 worker2 is running
    304 worker3 is running
    305 worker3 is running
    306 worker3 is running
    307 worker3 is running
    308 worker2 is running
    309 worker3 is running
    310 worker2 is running
    311 worker3 is running
    312 worker3 is running
    313 worker3 is running
    314 worker2 is running
    315 worker2 is running
    316 worker2 is running
    317 worker2 is running
    318 worker2 is running
    319 worker2 is running
    320 worker2 is running
    321 worker2 is running
    322 worker2 is running
    323 worker2 is running
    324 worker3 is running
    325 worker2 is running
    326 worker2 is running
    327 worker2 is running
    328 worker2 is running
    329 worker2 is running
    330 worker2 is running
    331 worker2 is running
    332 worker2 is running
    333 worker2 is running
    334 worker2 is running
    335 worker2 is running
    336 worker2 is running
    337 worker2 is running
    338 worker2 is running
    339 worker2 is running
    340 worker2 is running
    341 worker2 is running
    342 worker2 is running
    343 worker2 is running
    344 worker4 is running
    345 worker4 is running
    346 worker4 is running
    347 worker4 is running
    348 worker4 is running
    349 worker4 is running
    350 worker4 is running
    351 worker4 is running
    352 worker4 is running
    353 worker4 is running
    354 worker4 is running
    355 worker4 is running
    356 worker4 is running
    357 worker4 is running
    358 worker4 is running
    359 worker4 is running
    360 worker3 is running
    361 worker3 is running
    362 worker3 is running
    363 worker3 is running
    364 worker3 is running
    365 worker3 is running
    366 worker3 is running
    367 worker3 is running
    368 worker3 is running
    369 worker3 is running
    370 worker3 is running
    371 worker3 is running
    372 worker3 is running
    373 worker3 is running
    374 worker3 is running
    375 worker3 is running
    376 worker3 is running
    377 worker3 is running
    378 worker3 is running
    379 worker3 is running
    380 worker3 is running
    381 worker3 is running
    382 worker3 is running
    383 worker3 is running
    384 worker3 is running
    385 worker3 is running
    386 worker3 is running
    387 worker3 is running
    388 worker3 is running
    389 worker3 is running
    390 worker3 is running
    391 worker3 is running
    392 worker3 is running
    393 worker3 is running
    394 worker3 is running
    395 worker3 is running
    396 worker3 is running
    397 worker3 is running
    398 worker3 is running
    399 worker3 is running
    400 worker3 is running
    401 worker3 is running
    402 
    403 Process finished with exit code 0
    View Code

        测试:使用logging 日志输出实现线程安全:

     1 import threading
     2 import logging
     3 import time
     4 
     5 def work():
     6     for x in range(100):
     7         logging.warning('{} is running'.format(threading.current_thread().name))
     8 
     9 for x in range(1, 5):
    10     name = 'worker{}'.format(x)
    11     t = threading.Thread(name=name, target=work)
    12     t.start()
    使用logging模块

        结果:可以看到没有出现不确定结果,但是是stderr ,所以 日志处理模块是线程安全的。

      

    14、daemon 线程 和non-daemon线程

      注意:这里的daemon 不是Linux中的守护进程。

      进程靠线程执行代码,至少有一个主线程,其他线程是工作线程。

      主线程是第一个启动的线程,作为程序的入口

      父线程:如果线程A 中启动了一个线程B,A 就是B 的父线程

      子线程:B就是A 的子线程。

      Python中,构造线程的时候,可以设置daemon属性,这个属性必须在start方法前设置好。 

     1  def start(self):
     2         """Start the thread's activity.
     3 
     4         It must be called at most once per thread object. It arranges for the
     5         object's run() method to be invoked in a separate thread of control.
     6 
     7         This method will raise a RuntimeError if called more than once on the
     8         same thread object.
     9 
    10         """
    11         if not self._initialized:
    12             raise RuntimeError("thread.__init__() not called")
    13 
    14         if self._started.is_set():
    15             raise RuntimeError("threads can only be started once")
    16         with _active_limbo_lock:
    17             _limbo[self] = self
    18         try:
    19             _start_new_thread(self._bootstrap, ())
    20         except Exception:
    21             with _active_limbo_lock:
    22                 del _limbo[self]
    23             raise
    24         self._started.wait()
    25 
    26     def run(self):
    27         """Method representing the thread's activity.
    28 
    29         You may override this method in a subclass. The standard run() method
    30         invokes the callable object passed to the object's constructor as the
    31         target argument, if any, with sequential and keyword arguments taken
    32         from the args and kwargs arguments, respectively.
    33 
    34         """
    35         try:
    36             if self._target:
    37                 self._target(*self._args, **self._kwargs)
    38         finally:
    39             # Avoid a refcycle if the thread is running a function with
    40             # an argument that has a member that points to the thread.
    41             del self._target, self._args, self._kwargs
    start和run原码

      Thread的部分原码:

      

      从原码看出 daemon属性 是keyword-only ,其次 如果daemon是None 使用当前父线程的daemon,因为这个虽然说current_thread(),但是此时线程没有start,所以这的当前线程就是父线程。

      如果设定了,就是用户的设置。

      主线程是non-daemon线程,即daemon = False 也就是None,也就是 默认值。

      测试:non-daemon:

     1 import threading
     2 import time
     3 
     4 def foo():
     5     time.sleep(5)
     6     for i in range(20):
     7         print(i)
     8 # 主线程是non-daemon线程
     9 # t = threading.Thread(target=foo, daemon=False)
    10 # t = threading.Thread(target=foo, daemon=None)
    11 t = threading.Thread(target=foo)
    12 t.start()
    13 
    14 print('main thread exit')
    non-daemon

      结果:三者结果一样:但是注意None是看情况的,看Thread 原码

     1 main thread exit
     2 0
     3 1
     4 2
     5 3
     6 4
     7 5
     8 6
     9 7
    10 8
    11 9
    12 10
    13 11
    14 12
    15 13
    16 14
    17 15
    18 16
    19 17
    20 18
    21 19
    View Code

       测试:daemon = True

     1 import threading
     2 import time
     3 
     4 def foo():
     5     time.sleep(5)
     6     for i in range(20):
     7         print(i)
     8 # 主线程是non-daemon线程
     9 t = threading.Thread(target=foo, daemon=True)
    10 t.start()
    11 
    12 print('main thread exit')
    daemon=True

      结果:

    1 main thread exit
    2 
    3 Process finished with exit code 0
    View Code

      总结:

        线程 t 依然执行,主线程 已经执行完,但一直等着线程 t。

        如果daemon=True,主线程结束,程序立即结束,根本没有等线程 t

    名称            含义
    daemon属性 表示线程是否是daemon线程,这个值必须在start()之前设置,否则引发RuntimeError异常
    isDaaemon() 是否是daemon线程
    setDaemon 设置为daemon线程,必须在start方法之前设置

      

      总结:

        线程具有一个daemon属性,可以显示这只为True或False,也可以不设置,则取默认值None

        如果不设置daemon, 就取当前线程的daemon来设置它

        主线程是non-daemon 线程,即daemon=False

        从主线程创建的所有线程不设置daemon属性,则默认都是daemon= False,也就是non-daemon线程。

        Python程序在没有活着的non-daemon线程运行时退出,也就是剩下的只能是daemon线程,主线程才能退出,否则主线程只能等待。

      测试1:

      

      测试2 :

      

      测试 3:

      

      通过实验说明:

          如果有non-daemon线程的时候,主线程退出时,也不会杀掉daemon线程,知道所有的non-daemon线程全部结束,如果还有daemon线程,主线程需要退出,会结束所有的daemon线程,退出。也就是说,主线程退出时,有non-daemon就等,没有就退出。看下面的测试。

      测试:

      

      测试:

     1 import time
     2 import threading
     3 
     4 def work():
     5     time.sleep(10)
     6     print('worK')
     7 
     8 def bar():
     9     threading.Thread(target=work, daemon=False).start()
    10     time.sleep(5)
    11     print('bar')
    12 
    13 def foo():
    14     time.sleep(2)
    15     print('foo')
    16 
    17 t = threading.Thread(target=foo, daemon=False, name='outer1')
    18 t.start()
    19 
    20 for _ in range(10):
    21     t1 = threading.Thread(name='work', target=bar, daemon=True)
    22     t1.start()
    23 
    24 print('main thread exit')
    比较复杂的

      结果: 

     1 main thread exit
     2 foo
     3 bar
     4 barbar
     5 bar
     6 bar
     7 bar
     8 
     9 barbar
    10 
    11 bar
    12 bar
    13 worKworK
    14 worKworK
    15 worK
    16 worK
    17 
    18 
    19 worKworKworK
    20 
    21 worK
    View Code

      反正记住一条:主线程结束的时候,没有non-daemon 就结束程序。

    15、join方法:

      测试 1 加入join 和没有join比较

     1 import time
     2 import threading
     3 
     4 def foo(n):
     5     for i in range(n):
     6         print(i)
     7         time.sleep(1)
     8 
     9 t1 = threading.Thread(target=foo, args=(10,), daemon=True)
    10 t1.start()
    11 t1.join()
    12 
    13 print('main thread exit')
    View Code

      结果:  

    1 没有 join
    2 
    3 主线程结束,直接程序结束
    4 
    5 有join
    6 
    7 主线程等工作线程结束,在退出
    View Code

       测试 2:

      

      测试 3:

      

      测试 4:

      

      测试 5:

      

      总结:

        使用了join 方法后,daemon 线程执行完了,主线程才退出了。

        join(timeout=None), 是线程的标准方法之一

        一个线程中调用另一个线程的join方法,调用者将被阻塞,知道被调用线程终止。

        一个线程可以被join多次。

        timeout 参数指定调用者等待多久,没有设置超时,就一直等待到被调用线程结束。

        调用谁的join 方法,就是join 谁,就要等谁。

      daemon线程应用场景:

        简单来说就是,本来并没有daemon thread,为了简化程序员的工作,让他们不用去记录和管理那些后台线程,创造了一个daemon thread的概念,这个概念唯一的作用就是,当你把一个线程设置为daemon,它会随主线程的退出而退出,

        主要应用场景有:

      1. 后台任务,如发送心跳包,监控,这种场景最多。
      2. 主线程工作才有用的线程,如 主线程中维护这公共的资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适
      3. 随时可以被终止的线程。

        如果主线程退出,想所有其他工作线程一起退出,就使用daemon=True来创建工作线程。

        

        

         注:

          主线程M退出,进程就退出,子线程A创建的子线程B不受A影响

             主线程是non-daemon的

    16、threading.local类

      测试1:局部变量x

      

       测试 2:全局变量 x

      

       测试 3:全局变量 x 

       

       测试 全局变量 类实例对象的属性

     

       测试:python提供了一个threading.local类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其他线程看不见。

      

        和使用局部变量样的结果!

      测试: 

        测试:报错的时候:

       

      总结:

        从运行结果来看,另起一个线程打印ctx.x出错了,但是ctx 打印 没有出错,说明看到ctx,但是ctx中的x看不到,这个x不能夸线程。  

        threading.local 类构建了一个大字典,存放所有线程相关的字典,定义如下:(源码)   

    { id(Thread) -> (ref(Thread), thread-local dict) }

        每一个线程实例的id 为key,元组为vlaue

        value中2 部分,线程对象引用,每个线程自己的字典。

        本质:

          运行时,threading.local实例处在不同的线程中,就从大字典中找到了当前线程相关键值对中的字典,覆盖threading,.local实例的__dict__

          这样 就可以在不同的线程中,安全的使用线程独有的数据,做到了线程间数据隔离,如同本地变量一样安全。

     17、定时器 Timer / 延迟执行

     1 class Timer(Thread):
     2     """Call a function after a specified number of seconds:
     3 
     4             t = Timer(30.0, f, args=None, kwargs=None)
     5             t.start()
     6             t.cancel()     # stop the timer's action if it's still waiting
     7 
     8     """
     9 
    10     def __init__(self, interval, function, args=None, kwargs=None):
    11         Thread.__init__(self)
    12         self.interval = interval
    13         self.function = function
    14         self.args = args if args is not None else []
    15         self.kwargs = kwargs if kwargs is not None else {}
    16         self.finished = Event()
    17 
    18     def cancel(self):
    19         """Stop the timer if it hasn't finished yet."""
    20         self.finished.set()
    21 
    22     def run(self):
    23         self.finished.wait(self.interval)
    24         if not self.finished.is_set():
    25             self.function(*self.args, **self.kwargs)
    26         self.finished.set()
    threading.Timer原码

      threading.Timer 继承Thread, 这个类用来定义延迟多久后执行一个函数。

      

      start方法执行之后,Timer对象会处于等待状态,等待interval秒后,开始执行function函数

      测试:

      

       测试 cancel 1:

       

       测试 cancel 2:

       

       Timer提供了 cancel方法,用来取消 一个未执行的函数,如果上面的例子中worker函数已经开始执行,cancel就没有任何效果了。

       如:等待时间为0,线程刚开始就执行函数了,但是在执行cancle就没有取消效果了。

      

       总结:

        Timer是线程Thread的子类,就是线程类,具有线程的能力和特征。

        它的实例是能够延时执行目标函数的线程,在真正执行目标函数之前,都是可以cancel他的

        cancel方法本质使用Event 类 实现,并不是线程提供了取消方法。

    { id(Thread) -> (ref(Thread), thread-local dict) }
    为什么要坚持,想一想当初!
  • 相关阅读:
    cocos2dx的发展的例子2048(加入动画版)
    Hibernate操作Clob数据类型
    json级联城市
    Ubuntu Linux 永山(mount)分
    C++出现计算机术语5
    Cocos2d-x 3.0 红孩儿私人义务教育
    大页(huge pages) 三大系列 ---计算大页配置参数
    EJB_消息驱动发展bean
    HDU5086Revenge of Segment Tree(数论)
    第五章_JSTL
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9763515.html
Copyright © 2011-2022 走看看