• python -- 多线程编程


    多线程类似于同时执行多个不同程序,多线程运行有如下优点:

    • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
    • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
    • 程序的运行速度可能加快
    • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

    线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

    每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

    指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

    • 线程可以被抢占(中断)。
    • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

    python中的多线程

      多线程之间是共享内存的,而且是可以直接通信的。

    1、多线程实现

     1 '''1、直接实现(最常用)'''
     2 import threading
     3 import time
     4 
     5 def hello(num):
     6     print("This threading is no [%s]" %num)
     7     time.sleep(2)
     8     
     9 if __name__ == '__main__':
    10     '''
    11     t1 = threading.Thread(target=hello,args=[1,])  #实例化一个线程,后面参数为列表,只有一个元素时,要有','
    12     t2 = threading.Thread(target=hello,args=[2,])
    13     
    14     t1.start()  #上面只是实例化,这里才是启动线程
    15     t2.start()
    16     t1.join()
    17     t2.join()
    18     print('------main------')
    19     
    20     以上在没有t1.join()、t2.join()时,后面那句直接打印。因为启动线程后,主线程和两个子线程
    21 相互独立, print('------main------')是属于主线程的,不会等待子线程结束才打印
    22 
    23 t1.join() 的作用是:阻塞,等待线程t1结束后猜结束父线程
    24     '''
    25     
    26     #若有多个线程,对上面代码改进:
    27     t_list = []
    28     for i in range(10):
    29         t = threading.Thread(target=hello,args=[i,])
    30         t.start()
    31         t_list.append(t)
    32     
    33     for i in t_list:
    34         i.join()
    35     
    36     print('------main------')
    37     
    38     
    39 '''2、自定义类实现多线程'''
    40 import threading
    41 import time
    42 
    43 class MyThread(threading.Thread):
    44     def __init__(self,num):
    45         super(MyThread,self).__init__()     #继承父类的构造函数
    46         self.num = num
    47         
    48     #在自定义线程类里面,必须有这个run方法
    49     #这个run默认是每个线程调用start函数后执行的函数
    50     def run(self):
    51         print("This threading is no [%s]" %self.num)
    52         time.sleep(2)        
    53         
    54 
    55 t1 = MyThread(1)
    56 t2 = MyThread(2)
    57 t1.start()
    58 t2.start()
    59 t1.join()
    60 t2.join()
    61 print("父线程执行结束~")
    62 
    63 
    64 '''守护线程'''
    65 '''
    66 setDaemon  设置守护线程 eg:t.setDaemon(True)
    67 '''
    68 import time
    69 import threading
    70 
    71 def run(n):
    72     print("[%s] ------running------
    " %n)
    73     time.sleep(2)
    74     print('---done---')
    75     
    76 def main():
    77     for i in range(5):
    78         t = threading.Thread(target=run,args=[i,])
    79         t.start()
    80         print("starting thread",t.getName())
    81     t.join(1)
    82     
    83 m = threading.Thread(target=main,args=[])
    84 #将m线程设置为Daemon线程,它做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完任务
    85 m.setDaemon(True)  
    86 m.start()
    87 m.join(2)
    88 print("----main thread done---")

      注意:python的多线程是通过c语言的接口调用的系统原生的线程,所以在线程调用后,python就是去了对线程的控制能力,当线程结束后会把执行结果返回给python。

    2、GIL与线程锁    

        GIL锁:全局解释器锁,它是解释器层次的,作用是控制每次只能有一个python线程在操作系统中运行
        线程锁(互斥锁):是在解释器之上控制同一时间只能一个线程修改指定变量的锁

        在多线程时,若多个线程都是修改同一份数据,那么就要对修改数据的代码部分加上线程锁
        这样能使在同一时刻,只有一个线程在修改该数据,这样才能达到预想的效果

     1 import threading
     2 import time
     3 
     4 def addNum():
     5     global num
     6     print("--get num:",num)
     7     time.sleep(1)
     8     lock.acquire()    #加锁
     9     num -= 1   #修改变量
    10     lock.release()    #解锁   加锁后必须要解锁,不然程序就死了
    11     
    12 num = 100    
    13 thread_list = []
    14 lock = threading.Lock()    #实例化一个线程锁
    15 
    16 for i in range(100):
    17     t = threading.Thread(target=addNum)
    18     t.start()
    19     thread_list.append(i)
    20     
    21 for i in thread_list:      #等待所有线程都执行完毕
    22     #t.join(timeout=3)   #注意:这里设置了timeout后就不再是阻塞状态,而且,并不是等待3s...
    23     t.join()    #作用:只要子线程t还没执行结束,这里就阻塞,父线程就不能退出(父线程就是执行脚本这个)
    24 
    25 print('finally num:',num)
    26 '''
    27 没加锁时,各线程执行后可能会覆盖其他线程的结果,导致结果是随机的
    28 加锁后,每次都只有一个线程在操作数据,所以结果就是确定的
    29 '''
    30 print('All of thread was exec over~')

    3、递归锁

     1 #!/usr/bin/env python
     2 #coding:utf-8
     3 
     4 '''
     5 递归锁:就是大锁里面还要包含子锁
     6 '''
     7 import threading,time
     8 
     9 def run1():
    10     print("grab the first data")
    11     rlock.acquire()   #捕获(加锁)
    12     global num1
    13     num1 += 1
    14     rlock.release()   #释放(去锁)
    15     return num1
    16     
    17 def run2():
    18     print("geab the second data")
    19     rlock.acquire()
    20     global num2
    21     num2 += 1
    22     rlock.release()
    23     return num2
    24     
    25 def run3():
    26     rlock.acquire()
    27     res1 = run1()
    28     print("------btween run1 and run2------")
    29     res2 = run2()
    30     rlock.release()
    31     print("res1:%s , res2: %s" %(res1,res2))
    32     
    33 if __name__ == "__main__":
    34     num1,num2 = 0,0
    35     rlock = threading.RLock()    #实例化一个递归锁
    36     for i in range(10):
    37         t = threading.Thread(target=run3)
    38         t.start()
    39         
    40     while threading.active_count() != 1:
    41         print("剩余线程个数:",threading.active_count())
    42     else:
    43         print("剩余线程个数:",threading.active_count())
    44         print('------all threads done------')
    45         print("最后 num1:%s  num2: %s" %(num1,num2))

    4、信号量

     1 #!/usr/bin/env python
     2 #coding:utf-8
     3 '''
     4 信号量:互斥锁同时只允许一个线程更改数据,而Semaphore(信号量)是同时允许一定数量的线程更改数据
     5 '''
     6 import threading,time
     7 
     8 def run(n):
     9     semaphore.acquire()
    10     time.sleep(1)
    11     print("run the thread: %s
    " %n)
    12     semaphore.release()
    13     
    14 if __name__ == '__main__':
    15     
    16     semaphore = threading.BoundedSemaphore(3)  #最多允许3个线程同时运行
    17     for i in range(20):
    18         t = threading.Thread(target=run,args=[i,])
    19         t.start()
    20         
    21 while threading.active_count() != 1:
    22     #print('当前剩余线程数:',threading.active_count())
    23     pass
    24 else:
    25     print("------ all threads done ------")
    26     

    5、接收线程返回的数据

     1 #!/usr/bin/env python
     2 # coding:utf-8
     3 import time
     4 import threading
     5 
     6 data = []
     7 def func(n):
     8     time.sleep(3)
     9     data.append(n**n)
    10     print('hello',n)
    11     return n**n
    12 
    13 thread_list = []
    14 for i in range(20):
    15     #t = threading.Thread(target=func,args=[i,]) #这里传参时,参数以列表形式后面可以加',',也可以不加;以元组形式必须加
    16     t = threading.Thread(target=func,args=(i,)) 
    17     t.start()
    18     thread_list.append(t)
    19     
    20 #for i in thread_list:
    21     #i.join()
    22     #print('hello ',i)
    23 
    24 #print(thread_list)
    25 print(data)
    26 print('----main thread end----')

    6、event模块

     1 #!/usr/bin/env python
     2 # coding:utf-8
     3 
     4 '''
     5 通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
     6 个人理解:
     7     通过Event实例化一个对象,这个对象用set方法设置一直flag,所有线程都能访问flag,并根据flag不同的值做不同的处理
     8 '''
     9 import threading
    10 import time
    11 
    12 
    13 def light():
    14     if not event.isSet():
    15         event.set()   # set后,wait就不阻塞
    16     count = 0
    17     while True:
    18         if count < 10:
    19             print(' 33[42;1m--green light on---33[0m')
    20         elif count < 13:
    21             print(' 33[43;1m--yellow light on---33[0m ')
    22         elif count < 20:
    23             if event.isSet():
    24                 event.clear()
    25             print(' 33[41;1m--red light on---33[0m ')
    26         else:
    27             count = 0
    28             event.set() 
    29         time.sleep(1)
    30         count += 1
    31 
    32 
    33 def car(n):
    34 
    35     ''' 
    36     :param n:  给车辆的编号 
    37     :return:  默认返回值是None
    38     '''
    39 
    40     while True:
    41         time.sleep(1)
    42         if event.isSet():
    43             print('car [%s] is running...' % n)
    44         else:
    45             print('car [%s] is waiting for the red light...' % n)
    46             event.wait()
    47 
    48 if __name__ == "__main__":
    49     event = threading.Event()   # 实例化一个Event对象
    50     Light = threading.Thread(target=light)
    51     Light.start()
    52     for i in range(3):
    53         Car = threading.Thread(target=car,args=(i,))
    54         Car.start() 
  • 相关阅读:
    解决ios下的微信页面背景音乐无法自动播放问题
    vue Vue-cli 笔记
    document.documentElement和document.body区别介绍
    GD库使用小结---2
    GD库使用小结---1
    踩到两只“bug”
    CI加载流程小结
    文件加载---理解一个project的第一步
    缓存的使用
    小小的分页
  • 原文地址:https://www.cnblogs.com/xtsec/p/6964150.html
Copyright © 2020-2023  润新知