zoukankan      html  css  js  c++  java
  • python之路-day33-线程相关1

    一、线程的理论

      1、什么是线程

       1) 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程

        2)线程顾名思义,就是一条流水线工作的过程(流水线的工做需要电源,电源就相当于cpu),而一条流水线必须属于一个车间,

      一个车间的工作过程就是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间至少有一条流水线。 

        3)所以:进程只是用来把资源集中到一起(进程只是一个资源单位,或者时候资源集合),而线程才是cpu上的执行单位

        4) 多线程(即多个控制线程)的概念是,在一个进程中存在多个线程,多个线程共享该进程的地址空间,相当与一个车间内有

      多天流水线,都公用一个车间的资源。例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有

      的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。

      2、线程与进程的区别

      1)同一个进程内的多个线程共享该进程内的地址资源

      2)创建线程的开销要远小于创建进程的开销(创建一个进程,就是创建一个车间,设计到申请空间,而且在该空间

      内至少建一条流水线,但创建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小)

    二、开启线程的两种方式

      1、threading模块介绍

      multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因为不在详细的介绍

      2、开启线程的两种方式

     1 # 开启线程的两种方式
     2 
     3 # 方式一
     4 # from threading import Thread
     5 # import time
     6 #
     7 # def sayhi(name):
     8 #     time.sleep(2)
     9 #     print('%s say hello'%name)
    10 #
    11 # if __name__ == '__main__':
    12 #     t = Thread(target=sayhi,args=('egon',))
    13 #     t.start()
    14 #     print('主线程')
    15 
    16 # 方式二
    17 import time
    18 from threading import Thread
    19 
    20 class Sayhi(Thread):
    21     def __init__(self,name):
    22         super().__init__()
    23         self.name = name
    24 
    25     def run(self):
    26         time.sleep(2)
    27         print('%s say hello'%self.name)
    28 
    29 if __name__ == '__main__':
    30     t = Sayhi('egon')
    31     t.start()
    32     print('主线程')
    创建线程的两种方式

    三、多线程和多进程的区别

      1、谁的开启速度快

    from threading import Thread
    
    def work():
        print('hello')
    
    if __name__ == '__main__':
        t = Thread(target=work)
        t.start()
        print('主线程/主进程')
    

    执行结果如下,几乎t.start()的同时就将线程开启了,然后打印出了hello,证明  线程的创建开销极小

    hello
    主线程/主进程
    

      

    from multiprocessing import Process
    
    def work():
        print('hello')
    
    if __name__ == '__main__':
        #在主进程下开启子进程
        p = Process(target=work)
        p.start()
        print('主进程/主线程')
    
    # 执行结果如下,p.start()将开启进程的信号发给操作系统,操作系统要申请内存
    # 空间,拷贝父进程地址空间到子进程,开销远大于线程
    # 主进程/主线程
    # hello
    

      

      2、瞅一瞅 pid

     1 # 在主进程下开启多个线程,每个线程都跟主进程的pid一样
     2 # from threading import Thread
     3 # import os
     4 #
     5 # def work():
     6 #     print('hello',os.getpid())
     7 #
     8 # if __name__ == '__main__':
     9 #     t1 = Thread(target=work)
    10 #     t2 = Thread(target=work)
    11 #     t1.start()
    12 #     t2.start()
    13 #     print('主线程/主进程pid',os.getpid())
    14 #
    15 # 结果:
    16 # hello 9856
    17 # hello 9856
    18 # 主线程/主进程pid 9856
    19 
    20 # 开多个进程,每个进程都有不同的pid
    21 # from multiprocessing import Process
    22 # import os
    23 # 
    24 # def work():
    25 #     print('hello',os.getpid())
    26 # 
    27 # if __name__ == '__main__':
    28 #     p1 = Process(target=work)
    29 #     p2 = Process(target=work)
    30 #     p1.start()
    31 #     p2.start()
    32 #     print('主进程/主线程',os.getpid())
    33 # 
    34 # 结果:
    35 # 主进程/主线程 38384
    36 # hello 37008
    37 # hello 36496
    线程、进程中的pid

      3、同一进程内的线程共享该进程的数据?

     1 # 进程之间地址空间是隔离的
     2 # from multiprocessing import Process
     3 # import os
     4 #
     5 # def work():
     6 #     global n
     7 #     n = 0
     8 #
     9 # if __name__ == '__main__':
    10 #     n = 100
    11 #     p = Process(target=work)
    12 #     p.start()
    13 #     p.join()
    14 #     print('主',n)
    15 #
    16 # 结果分析:毫无疑问子进程p已将自己的全局n改成了0.但是改的仅仅是自己的
    17 # 父进程的n仍然为100
    18 
    19 
    20 #  同一进程内开启的多个线程是共享该进程地址空间的
    21 # from threading import Thread
    22 # import os
    23 # 
    24 # def work():
    25 #     global n
    26 #     n = 0
    27 # if __name__ == '__main__':
    28 #     n = 100
    29 #     t = Thread(target=work)
    30 #     t.start()
    31 #     t.join()
    32 #     print('主',n)
    33 # 结果:查看结果为0,因为统一进程内的线程之间共享进程内的数据
    34 # 主 0
    进程与进程进程与线程之间数据问题

    四、Thread 对象的其他属性或方法

      介绍:

    # isAlive():    返回线程是否活动的。
    # getName():    返回线程名。
    # setName():    设置线程名
    
    threading 模块提供的一些方法:
    
    # threading.currentThread():    返回当前线程变量
    # threading.enumerate():    返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
    # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
    

      验证:

     1 # from threading import Thread
     2 # import threading
     3 # from multiprocessing import Process
     4 # import os,time
     5 #
     6 # def work():
     7 #     time.sleep(3)
     8 #     print(threading.current_thread().getName())
     9 # if __name__ == '__main__':
    10 #     # 在主进程下开启线程
    11 #     t = Thread(target=work)
    12 #     t.start()
    13 #     print(threading.current_thread().getName())
    14 #     print(threading.current_thread())
    15 #     print(threading.enumerate())
    16 #     print(threading.activeCount)
    17 #     print('主进程主线程')
    18 #
    19 # 结果:
    20 # MainThread
    21 # <_MainThread(MainThread, started 39404)>
    22 # [<_MainThread(MainThread, started 39404)>, <Thread(Thread-1, started 39468)>]
    23 # <function active_count at 0x000001CBF2F45BF8>
    24 # 主进程主线程
    25 # Thread-1
    Thread对象的其他属性方法
     1 # 主线程等待子进程结束
     2 # from threading import Thread
     3 # import time
     4 # def sayhi(name):
     5 #     time.sleep(2)
     6 #     print('%s say hello'%name)
     7 # if __name__ == '__main__':
     8 #     t = Thread(target=sayhi,args=('egon',))
     9 #     t.start()
    10 #     t.join()
    11 #     print('主进程')
    12 #     print(t.is_alive())
    13 # 
    14 # 执行结果:
    15 # egon say hello
    16 # 主进程
    17 # False
    主线程等待子线程结束

    五、守护线程

      无论是进程还是线程,都遵循:守护xxx会主动等待主xxx运行完毕后被销毁

      需要强调的是:运行完毕并非终止运行

      

    1、对主进程来说,运行完毕指的是主进程代码运行完毕
    
    2、对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
    
    
    详细解释:
    1、主进程在其代码结束后就已经运算完毕了(守护进程在此时就被回收),然后主进程
    会一直等飞受贿的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才结束
    
    2、主线程在其他费守护线程运行完毕后才算运行完毕(守护线程在刺死就被回收)。因为主线程的结束意味着进程的结束,基础讷航整体的资源都将被回收,而基础讷航必须保证非守护线程都运行完毕后才能结束。
    

      验证:

    # from threading import Thread
    # import time
    # def sayhi(name):
    #     time.sleep(2)
    #     print('%s say hello'%name)
    # 
    # if __name__ == '__main__':
    #     t = Thread(target=sayhi,args=('egon'))
    #     t.setDaemon(True) # 必须在t.start()之前设置
    #     t.start()
    #     print('主线程')
    #     print(t.is_alive())
    # 
    # 执行结果:
    # 主线程
    # True
    守护线程验证

    六、死锁现象与递归锁

      1、所谓死锁:是指两个或两个以上的进程或线程执行过程中,因为争夺资源而造成的一种互相等待的现象,若无外力

      作用,它们都将无法推进下去。此时称系统处于死锁或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

     1 # from threading import Thread,Lock
     2 # import time
     3 # 
     4 # muteA = Lock()
     5 # muteB = Lock()
     6 # 
     7 # class MyThread(Thread):
     8 #     def run(self):
     9 #         self.fun1()
    10 #         self.fun2()
    11 # 
    12 #     def fun1(self):
    13 #         muteA.acquire()
    14 #         print('%s拿到A锁'%self.name)
    15 # 
    16 #         muteB.acquire()
    17 #         print('%s 拿到B锁'%self.name)
    18 #         muteB.release()
    19 #         muteA.release()
    20 # 
    21 #     def fun2(self):
    22 #         muteB.acquire()
    23 #         print("%s 拿到B锁"%self.name)
    24 #         time.sleep(2)
    25 # 
    26 #         muteA.acquire()
    27 #         print("%s 拿到A锁"%self.name)
    28 #         muteA.release()
    29 # 
    30 #         muteB.release()
    31 # 
    32 # if __name__ == '__main__':
    33 #     for i in range(10):
    34 #         t = MyThread()
    35 #         t.start()
    36 #         
    37 # 结果:
    38 # Thread-1拿到A锁
    39 # Thread-1 拿到B锁
    40 # Thread-1 拿到B锁
    41 # Thread-2拿到A锁
    死锁

      2、递归锁

      解决办法:递归锁,在python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock个RLock内部维护着

    一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都

    被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁,二者的区别是:递归锁可以连续

    acquire多次,而互斥锁只能acquire一次

     1 from threading import Thread,RLock
     2 import time
     3 
     4 mutexA=mutexB=RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止
     5 
     6 class MyThread(Thread):
     7     def run(self):
     8         self.func1()
     9         self.func2()
    10     def func1(self):
    11         mutexA.acquire()
    12         print('33[41m%s 拿到A锁33[0m' %self.name)
    13 
    14         mutexB.acquire()
    15         print('33[42m%s 拿到B锁33[0m' %self.name)
    16         mutexB.release()
    17 
    18         mutexA.release()
    19 
    20     def func2(self):
    21         mutexB.acquire()
    22         print('33[43m%s 拿到B锁33[0m' %self.name)
    23         time.sleep(2)
    24 
    25         mutexA.acquire()
    26         print('33[44m%s 拿到A锁33[0m' %self.name)
    27         mutexA.release()
    28 
    29         mutexB.release()
    30 
    31 if __name__ == '__main__':
    32     for i in range(10):
    33         t=MyThread()
    34         t.start()
    递归锁
  • 相关阅读:
    不删除数据库,只删除GridView的某一行!
    纯CSS无图打造圆角Table 无图制作圆角
    2009年总结与2010总体计划
    工作中的碰到的问题,以及处理过程:
    SQL Server 2005 不允许远程连接解决方法
    Visual Studio 2008项目模板丢失的解决办法
    C#格式化数值结果表
    准备把csdn的博客搬到这里
    生产系统中 RAC 数据库服务器 不要批量 gzip压缩
    不再更新的业务统计表
  • 原文地址:https://www.cnblogs.com/alvin-jie/p/10046536.html
Copyright © 2011-2022 走看看