• 多线程


    线程的特点

    线程是程序运行中的最基本单元,在一个进程内有且至少有一个进程。

     进程:内存独立,CPU独立,速率低共享数据难,安全性高

     线程:内存共享,CPU使用独立,是cpu最小的执行单位

    线程的创建方法

    1.直接实例化Thread类

       t = Thread(target=task)
        t.start()
    
        task()
        #执行顺序不固定 如果开启线程速度足够快  可能子线程先执行
        print("主线程over")
        print("1",current_thread())

    2.创建类覆盖Thread中的run方法

    class MyThread(Thread):
        def run(self):
            print("子线程run!")
    m = MyThread()
    m.start()
    print("主线over")

    使用方法和进程一样,但是开启线程的代码可以放在任何位置,开启进程的方法只能放在__name__==__main__中

    在同一进程中线程间数据共享

    from threading import Thread
    
    a = 10
    def task():
        global a
        print("子 running...")
        a = 20
    
    t1 = Thread(target=task)
    t1.start()
    
    t1.join() # 主线程等待子线执行完毕
    print(a)
    
    ==》
    子 running...
    20

    进程与线程效率

    开启一百个进程

    def task():
        pass
    
    if __name__ == '__main__':
        st_time=time.time()
        for a in range(100):
            
            t=Process(target=task)
            t.start()
    
        print(time.time()-st_time)
    
    ==3.447035074234009  开启一百个进程花费的时间

    开启一百个线程

    def task():
        pass
    
    if __name__ == '__main__':
        st_time = time.time()
        for a in range(100):
    
            t=Thread(target=task)
            t.start()
    
        print(time.time()-st_time)
        print('over')
    ==0.014977693557739258

    由此可见线程的开销远远小于进程的开销

    守护线程

    def task():
    print('1run')
    time.sleep(3)
    print('1over')

    def task1():
    print('2run')
    time.sleep(6)
    print('2over')

    t=Thread(target=task)
    t1=Thread(target=task1)
    t.start()
    t1.daemon=True
    t1.start()
    print('over')
    ==>
    1run
    2run
    over
    1over

    主线程代码执行完后不会立即结束,会等待其他非守护进程

    死锁

    当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源要想使用必须同时具备两把锁这时候程序就会进程无限卡死状态 ,这就称之为死锁

    lock1 = Lock()
    
    # 筷子
    lock2 = Lock()
    
    def eat1():
        lock1.acquire()
        print("%s抢到了盘子" % current_thread().name)
        time.sleep(0.5)
        lock2.acquire()
        print("%s抢到了筷子" % current_thread().name)
    
        print("%s开吃了!" % current_thread().name)
        lock2.release()
        print("%s放下筷子" % current_thread().name)
    
        lock1.release()
        print("%s放下盘子" % current_thread().name)
    
    def eat2():
        lock2.acquire()
        print("%s抢到了筷子" % current_thread().name)
    lock1.acquire()
    print("%s抢到了盘子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) t1 = Thread(target=eat1) t2 = Thread(target=eat2) t1.start() t2.start() ==》 Thread-1抢到了盘子 Thread-2抢到了筷子

    如上所示,两个锁可以看作两个许可,线程需要同时拿到两把锁才可以正常运行,当两个线程各拿到一把锁的时候双方都无法正常进行下去,这就出现了死锁

    如何避免死锁问题  
            锁不要有多个,一个足够
            如果真的发生了死锁问题,必须迫使一方先交出锁

    Rlock

    称之为递归锁或者可重入锁,Rlock不使用用来解决死锁问题的

    与Lock唯一的区别:
        Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次
        如果一个线程已经执行过acquire 其他线程将无法执行acquire

    信号量

    可以现在被锁定的代码 同时可以被多少线程并发访问

    用途: 仅用于控制并发访问   并不能防止并发修改造成的问题

    from threading import Semaphore, Thread
    import time
    
    s = Semaphore(5)
    def task():
        s.acquire()
        print("子run")
        time.sleep(3)
        print("子over")
        s.release()
    
    for i in range(10):
        t = Thread(target=task)
        t.start()
  • 相关阅读:
    JSP学习笔记(一):JSP语法和指令
    小知识随手记(五)
    jQuery序列化表单数据 serialize()、serializeArray()及使用
    Servlet学习笔记(三):HTTP请求与响应
    Servlet学习笔记(二):表单数据
    AJAX前台传过来的中文在后台获取是乱码问题
    Servlet学习笔记(一):生命周期
    Servlet介绍以及简单实例
    jsp+servlet+jdbc实现对数据库的增删改查
    UML类图符号解释
  • 原文地址:https://www.cnblogs.com/duGD/p/10976153.html
Copyright © 2020-2023  润新知