• 线程的理论知识 开启线程的两种方式(Thread) 线程和进程之间的对比 线程的其他方法 守护进程 互斥锁 死锁现象,递归锁 信号量


    day34

    一丶线程的理论知识

    什么是线程:

    1.线程是一堆指令,是操作系统调度的最小单位

    2.线程具有执行能力

    3.线程依赖于进程

    4.具有主从关系(人为定义,每一个进程都至少有一个主线程

     

    二丶开启线程的两种方式(Thread)

    类的方式开启线程

    ### 利用到Thread

    from  threading import Thread

    class MyThread(Thread):
     
       def run(self) -> None: # 必须重写run函数
           print(f'{self.name} 被开启了~~')
           
           
    if __name__ == '__main__':
       t=MyThread() # 实例化一个自定义线程类对象
       t.start() # 启动子线程
       print('in 主线程')

     

    函数的方式开启线程

    ### 函数开启线程
    from  threading import Thread

    def task(name):
       print(f'在子线程中: {name}')


    if __name__ == '__main__':
       
       t=Thread(target=task,args=('abc',))   # 实例化线程对象
       t.start() # 启动子线程
       
       print('主~~~~')

     

    三丶线程和进程之间的对比

    进程VS线程:

    1.线程的启动速度 快于 进程的启动速度

    2.线程之间数据可以共享,进程之间不能数据共享(必须依靠队列才能实现)

    3.线程开销小,进程开销大

    4.在运行速度上,进程和线程是没有可比性.(两个不同的概念,线程具有执行能力,进程不具有执行能力)

    四丶线程的其他方法

    两种:

    1.线程对象方法

    2.threading对象方法

    # -*-coding:utf-8-*-
    # Author:Ds

    from threading import Thread
    import threading
    import time


    ### 定义开启子线的方法
    def task():
       time.sleep(1)       # 睡1秒, 保证所有的子线程都能存活1秒 以上
       print(f'123')
       print(threading.current_thread().name)  #子线程对象名字


       
    if __name__ == '__main__':
       for i in range(6):
           t=Thread(target=task,name='firstIn') #target指定子线程任务,name子线程名
           t.start()

           
           
       ### 1. 线程对象方法
       print(t.isAlive())  # 判断线程是否还存活
       t.setName('32141') # 设置name属性
       print(t.getName()) # 获得线程名

       

       ### 2 threading模块方法
       print(threading.current_thread())  #线程对象
       print(threading.enumerate())        # 列表 [<_MainThread(MainThread, started 10748)>, <Thread(firstIn, started 3696)>,]
       print(threading.active_count())     # 获取活跃的线程数量 (包括主线程 ) 7个
       print('主线程')

     

    五丶守护进程

    什么是守护线程:

    1.守护线程必须等待所有的非守护线程以及主线程结束之后才结束

    2.本质还是子线程,在开启前被设置成守护线程.

    ### 守护线程

    from threading import Thread
    import time

    def task(name):
       print(f'{name} is running')
       time.sleep(1)               # 当主线程结束了,当前守护线程也就结束了
       print(f'{name} is over')



    if __name__ == '__main__':
       t = Thread(target=task,args=('abc',))
       t.daemon = True     # 将一个子线程设置为守护线程
       t.start()

       print('主线程')

    容易产生歧义:

    1.首先 要明确, 在同一时刻, CPU只允许一个任务的存在, 遇到IO阻塞进行任务切换

    2.守护进程VS守护线程

    守护线程:必须等待所有的非守护线程执行完毕以及主线程执行完毕,才能结束

    守护进程:守护进程不会等待所有的非守护进程完毕才结束.只要主进程GG,守护进程GG

    ### 守护线程:   

    from threading import Thread
    import time

    def foo():
       print(123)
       time.sleep(1)       # sleep 表示要切换, 执行bar子线程
       print("end123")

    def bar():
       print(456)          # 执行 守护线程必须等待非守护线程执行完毕才能结束
       time.sleep(3)
       print("end456")

    if __name__ == '__main__':

       t1=Thread(target=foo)
       t2=Thread(target=bar)

       t1.daemon = True
       t1.start()
       t2.start()
       print("main-------")

     

    六丶互斥锁

    含义:

    1.互斥锁,同步锁,锁.都是同一种锁LOCK

    2.在并发时,保证数据的安全('串行')

    ###互斥锁 实现 '并发' 

    # -*-coding:utf-8-*-
    # Author:Ds

    from threading import Thread
    from threading import Lock      # 这是线程的 互斥锁

    import  time

    x=100

    def task(lock):
       lock.acquire()      # 加锁 实现 '并发' ,保证数据的安全性
       global x
       temp = x
       time.sleep(0.1)
       temp -= 1
       x = temp
       print(x)
       lock.release()


    if __name__ == '__main__':
       lock=Lock() # 实例化锁对象

    #### 如果想要实现并发. 1.把所有的实例对象都添加列表中,2 join循环列表, 每一个线程都必须等待上一个线程执行完,必须等待上一个线程执行完

       t_li=[]  # 存放线程对象, 实现并发
       for i in range(100):        # 创建100个子线程
           t=Thread(target=task,args=(lock,))
           t_li.append(t)
           t.start()
       for el in t_li:
           el.join()               # 循环列表, 每一个线程都必须等待上一个线程执行完,必须等待上一个线程执行完
       print(f'最后x:{x}')

     

    七丶死锁现象,递归锁

    死锁含义(Lock):

    一个资源被多次调用,多次调用资源未能释放,会造成一种互相等待的现象.在没有外力作用下,只能停留在这,此时的系统处于锁死状态.

    死锁现象(2种):

    1.当一个进程或者一个线程一直占调用或者占用同一把锁Lock时,而不释放资源会导致其他进程/线程无法获得锁,就会出现锁死现象.一直出去阻塞acquire()状态

    代码见:

    ### 当一个锁对象已经被上锁, 试图再次加锁,  就会造成锁死.
    from multiprocessing import Lock

    def task1(loc):
       print('task1')
       loc.acquire()
       print('task1: 开始打印')
       time.sleep(random.randint(1,3))
       print('task1: 结束打印')
       loc.release()

    def task2(loc):
       print('task2')
       loc.acquire()                        # 第一层锁  
       loc.acquire()                        #第二层锁, 试图再次加锁,由于锁对象已经被占用(已经锁上了,还没有释放)再次上锁,就会造成锁死 (程序被卡主)~~~
       loc.release()
       print('task2: 开始打印')
       time.sleep(random.randint(1,3))
       print('task2: 结束打印')
       loc.release()


    def task3(loc):
       print('task3')
       loc.acquire()
       print('task3: 开始打印')
       time.sleep(random.randint(1,3))
       print('task3: 结束打印')
       loc.release()


    if __name__ == '__main__':
       loc=Lock()
       p1=Process(target=task1,args=(loc,)).start()
       p2=Process(target=task2,args=(loc,)).start()
       p3=Process(target=task3,args=(loc,)).start()
       

     

    2.当有两个进程/线程同时想要获取两个锁时,由于两者都是处于竞争关系.就可能会出现两者都阻塞在同一放,都无法同时获得两个锁 或者 要获取对方已经获得的还没有释放的锁. 代码如下

    # -*-coding:utf-8-*-
    # Author:Ds

    from threading import  Thread
    from threading import Lock
    import  time

    # 两把 不一样的锁
    lock_A=Lock()
    lock_B=Lock()

    class MyThread(Thread):

       def run(self) -> None:
           self.f1()
           self.f2()

       def f1(self):
           lock_A.acquire()
           print(f'{self.name} 拿到A锁')

           lock_B.acquire() #
           print(f'{self.name} 那到B锁')
           lock_B.release()

           lock_A.release()

       def f2(self):
           lock_B.acquire()
           print(f'{self.name} 拿到B锁')

           time.sleep(1)
           lock_A.acquire()
           print(f'{self.name} 拿到A锁')
           lock_A.release()

           lock_B.release()

    if __name__ == '__main__':

       for i in range(2):   # 实例化两个线程对象
           t=MyThread() # 简单命名,线程1和线程2
           t.start()

       print('in 主线程')

       
    ### 口述上述代码的过程:
       #1. 线程 1 执行f1函数, 拿到A锁,   线程2 也要拿A锁. 由于线程1已经拿到A锁,线程2必须等待A锁
       #2. 线程1 继续执行f1函数 ,此时 B锁没有人使用. 轻松的执行完 f1函数
       #3. 重点: 线程1执行f2函数 拿走B锁.     而此时A锁已经被释放了,现在线程2拿走A锁,
       #4. 重点: 线程1执行f2函数 已经拿走B锁,继续执行需要A锁.   此时发现A锁已经被线程2拿走了, 线程2 执行f1函数,已经拿走了A锁,需要B锁.而此时B锁在线程1中.
       ### 由于竞争关系的存在. 两个线程对同一个资源进行抢夺. 一直卡在acquire() , 造成死锁.

     

    递归锁(RLock):

    1.防止线程锁死

    2.引用计数原则:非0不能被抢,引用1次锁一次,引用N次锁N次.

    3.在并发时,保证数据的安全('串行')

    # -*-coding:utf-8-*-
    # Author:Ds
    
    ### 递归锁是一把锁,锁上有记录,只要acquire一次,锁上就计数1次, acquire2次,锁上就计数2次,
    # release1次,减一,
    # 只要递归锁计数不为0,其他线程不能抢.
    
    
    from threading import  Thread
    from threading import RLock
    import  time
    
    
    # 同一个锁对象,两把一样的锁 ,内部采用单例模式
    lock_A=lock_B=RLock()
    
    
    class MyThread(Thread):
    
        def run(self) -> None:
            self.f1()
            self.f2()
    
        def f1(self):
            lock_A.acquire()
            print(f'{self.name}  拿到A锁')
    
            lock_B.acquire() #
            print(f'{self.name} 那到B锁')
            lock_B.release()
    
            lock_A.release()
    
        def f2(self):
            lock_B.acquire()
            print(f'{self.name} 拿到B锁')
    
            time.sleep(1)
            lock_A.acquire()
            print(f'{self.name} 拿到A锁')
            lock_A.release()
    
            lock_B.release()
    
    if __name__ == '__main__':
    
        for i in range(2):   # 实例化两个线程对象
            t=MyThread()
            t.start()
    
        print('in 主线程')
    
    

     

    八丶信号量

    含义:

    信号量允许多个线程或者进程同时进入

    描述:

    描述:一个网吧,有三台电脑,一开始三台电脑都没有人.这时候来了5个人要上网,网管允许3个人进入网吧,使用电脑. 剩下的2个人就必须在门外等待,此后来的认也要在门外等待. 如果这时候有1个人已经上完网了,网管得知后,打开网吧的门,让后面的1个人进入网吧上网.如果这时候又有2个人已经上网完毕离开了,网管又打开门,让后面的2个人进入网吧上网.

    在网吧系统中,上网电脑: 属于公共资源,上网的人属于一个线程, 网管就是起着信号量的作用

    # -*-coding:utf-8-*-
    # Author:Ds
    
    ### 信号量: 同一时间,只允许指定数量的线程工作.(多余的线程排队等候)
    ### 信号量允许多个线程或者进程同时进入
    
    from  threading import Thread
    from  threading import current_thread
    from  threading import Semaphore
    
    import time
    import random
    
    sm=Semaphore(3) # 定义信号量对象 同一时刻只允许3个任务被处理
    
    def Safe_Internet():
        sm.acquire()
    
        print(f'{current_thread().name} 正在上网')
        time.sleep(random.randint(1,3)) # 增加随机性
        sm.release()
        print(f'33[0;35m  {current_thread().name} 已经上完网了 33[0m')
    
    if __name__ == '__main__':
        t_l=[]
        for i in  range(20):
            t=Thread(target=Safe_Internet,)
            t_l.append(t)
            t.start()
    
        for i in t_l:
            i.join()
    
        print('in 主线程~~~~')
    
    
  • 相关阅读:
    Codeforces 1439B. Graph Subset Problem (思维,复杂度分析)
    石子游戏(nim游戏+按位考虑)
    Codeforces 1437F Emotional Fishermen(思维,dp)
    Codeforces Round #671 (Div. 2) (A~E)
    Java就业企业面试问题ssh框架
    DUBBO初探搭建DUBBO开发环境
    Spring容器组建注解@Component和Resouces实现完全注解配置
    jUnit 4 在 s2sh 中的应用
    4.5、常量、作用域、内置全局变量
    Java 诗词纵向转换字符流输出
  • 原文地址:https://www.cnblogs.com/xiangwang1/p/12180124.html
Copyright © 2020-2023  润新知