zoukankan      html  css  js  c++  java
  • day34:线程&守护线程&线程锁&事件

    目录

    1.线程的基本使用

    2.用类定义线程

    3.线程相关的函数

    4.守护线程

    5.线程中安全问题:Lock

    6.信号量:Semaphore

    7.死锁 互斥锁 递归锁

    8.事件:Event

    线程的基本使用

    首先,明确一下线程和进程的定义

    进程:资源分配的最小单位

    线程:cpu执行程序的最小单位

    1.一个进程资源中可以包含多个线程

    def func(num):
        time.sleep(random.uniform(0.1,1))
        print("当前进程{},参数是{}".format(os.getpid(),num))
    
    
    for i in range(10): # 在一个进程资源中创建了十个线程
        t = Thread(target=func,args=(i,))
        t.start()
    
    print(os.getpid())

    运行结果如下图所示

    2.并发的多线程和多进程谁的速度快? 多线程!

    计算多线程执行时间

    def func(num):
        print("当前进程{},参数是{}".format(os.getpid(), num))
    
    
    if __name__ == "__main__":
        # 多线程
        lst = []
        # 记录开始时间
        startime = time.time()
        for i in range(1000):
            t = Thread(target=func,args=(i,))
            t.start()
            lst.append(t)
    
        # 等到所有的子线程执行完毕
        for i in lst:
            i.join()
    
        # 计算结束时间
        endtime = time.time()
        print("多线程执行时间",(endtime - startime)) # 0.27

    如图所示,跑1000个线程只需要0.27s

    计算多进程执行时间

    def func(num):
        print("当前进程{},参数是{}".format(os.getpid(), num))
    
    
    if __name__ == "__main__":
        lst = []
        startime = time.time()
        for i in range(1000):
            t = Process(target=func, args=(i,))
            t.start()
            lst.append(t)
    
        # 等到所有的子线程执行完毕
        for i in lst:
            i.join()
    
        # 计算结束时间
        endtime = time.time()
        print("多进程执行时间", (endtime - startime))  # 63.30

    如图所示,跑1000个进程需要63.30s

    所以,我们得出结论!并发的多线程要远比多进程快!!!

    3.多线程之间,共享同一份进程资源

    num = 1000
    def func():
        global num
        num -= 1
        
    for i in range(1000):
        t = Thread(target=func)
        t.start()
        
    print(num)
        

    运行结果如下图所示

    用类定义线程

    class MyThread(Thread):
        def __init__(self,name):
            # 手动调用父类的构造方法
            super().__init__()
            self.name = name
            
        def run(self):
            time.sleep(1)
            print("当前进程号码是{},名字是{}".format(os.getpid() , self.name))
    
    
    if __name__ == "__main__":
        t = MyThread("当前是一个线程")
        t.start()
        print("主线程执行结束 ... ")

    线程相关的函数

    1.线程.is_alive() 检测线程是否仍然存在

    2.线程.setName() 设置线程名字

    3.线程.getName() 获取线程名字

    def func():
        time.sleep(1)
    
    if __name__ == "__main__":
        t = Thread(target=func)
        t.start()
        # 检测线程是否仍然存在
        print(t.is_alive()) # True
        # 获取线程名字
        print(t.getName())  # Thread-1
        # 设置线程名字
        t.setName("xboyww")
        # 获取线程名字
        print(t.getName()) # xboyww

    1.currentThread().ident 查看线程id号

    def func():
        print("子线程的线程id{}".format(currentThread().ident))
    
    if __name__ == "__main__":
        Thread(target=func).start()
        print("主线程的线程id{}".format(currentThread().ident))

    运行结果如下图所示

    2.enumerate() 返回目前正在运行的线程列表

    3.activeCount() 返回目前正在运行的线程数量

    def func():
        print("子线程的线程id{}".format(currentThread().ident))
        time.sleep(0.5)
    
    
    if __name__ == "__main__":
        for i in range(10):
            Thread(target=func).start()
        lst = enumerate()
        # 主线程 + 10个子线程
        print(lst, len(lst))
    
        # 3.activeCount() 返回目前正在运行的线程数量
        print(activeCount())  # 11

    运行结果如下图所示

    守护线程

    守护线程和守护进程不同

    守护进程是守护主进程,主进程结束,守护进程立刻被杀死

    守护线程是守护所有线程,必须等所有线程结束,守护线程才会被杀死

    def func1():
        while True:
            time.sleep(0.5)
            print("我是func1")
    
    def func2():
        print("我是func2 start ... ")
        time.sleep(3)
        print("我是func2 end ... ")
    
    def func3():
        print("我是func3 start ... ")
        time.sleep(5)
        print("我是func3 end ... ")
    
    if __name__ == "__main__":
        t1 = Thread(target=func1)
        t2 = Thread(target=func2)
        t3 = Thread(target=func3)
        
        # 在start调用之前,设置线程为守护线程
        t1.setDaemon(True)
        
        t1.start()
        t2.start()
        t3.start()
        
        print("主线程执行结束 .... ")

    运行结果如下图所示

    线程中安全问题:Lock

    现在,我们想完成如下的操作:

      1.在线程p1中for循环100万次,每次完成一次+1操作

      2.在线程p2中for循环100万洗,每次完成一次-1操作

    根据想法,我们可以写出如下代码:

    n = 0
    
    
    def func1(lock):
        global n
        for i in range(1000000):
            n += 1
    
    
    # lock.release()
    
    def func2(lock):
        global n
        for i in range(1000000):
            n -= 1
    
    
    if __name__ == "__main__":
        lst = []
        lock = Lock()
    
        startime = time.time()
        for i in range(10):
            t1 = Thread(target=func1, args=(lock,))
            t2 = Thread(target=func2, args=(lock,))
            t1.start()
            t2.start()
            lst.append(t1)
            lst.append(t2)
    
        for i in lst:
            i.join()
    
        endtime = time.time()
        print("主线程执行结束 ...  打印{} 时间是{}".format(n, endtime - startime))

    执行结果如下图所示

    然而,这并不是我们想要的结果

    所以我们需要在线程t1和线程t2加锁

    运行代码,发现得到的结果正是我们想要的结果

    信号量:Semaphore

    线程的Semaphore和进程的Semaphore完全一致,在此就不过多赘述了

    def func(i,sm):
        # 上锁 + 解锁
        with sm:
            print(i)
            time.sleep(3)    
    if __name__ == "__main__":
        # 支持同一时间,5个线程上锁
        sm = Semaphore(5)
        for i in range(20):
            Thread(target=func,args=(i,sm)).start()
        
    """
    再创建线程的时候是异步创建
    在执行任务时,遇到Semaphore进行上锁,会变成同步程序
    """

    死锁 互斥锁 递归锁

    1.语法上的死锁

    只上锁不解锁,一定会产生死锁

    lock = Lock()
    lock.acquire()
    lock.acquire()
    lock.acquire() # 只上锁不解锁,会产生死锁。运行程序会发生阻塞
    
    lock.release()
    print(1)

    2.逻辑上的死锁

    noodle_lock = Lock()
    kuaizi_lock = Lock()
    
    
    def eat1(name):
        noodle_lock.acquire()
        print("%s 抢到面条了" % (name))
        kuaizi_lock.acquire()
        print("%s 抢到筷子了" % (name))
    
        print("开始享受面条 ... ")
        time.sleep(0.5)
    
        kuaizi_lock.release()
        print("%s 放下筷子" % (name))
        noodle_lock.release()
        print("%s 放下面条" % (name))
    
    
    def eat2(name):
        kuaizi_lock.acquire()
        print("%s 抢到筷子了" % (name))
        noodle_lock.acquire()
        print("%s 抢到面条了" % (name))
    
        print("开始享受面条 ... ")
        time.sleep(0.5)
    
        noodle_lock.release()
        print("%s 放下面条" % (name))
        kuaizi_lock.release()
        print("%s 放下筷子" % (name))
    
    
    if __name__ == "__main__":
        name_lst1 = ["Fly", "Hurt"]
        name_lst2 = ["Alan", "Cat"]
    
        for name in name_lst1:
            Thread(target=eat1, args=(name,)).start()
    
        for name in name_lst2:
            Thread(target=eat2, args=(name,)).start()

    运行结果如下图所示

    3.递归锁

    上方的示例造成了逻辑上的死锁现象

    想要解决这种情况,我们需要递归锁。

    什么是递归锁?

      递归锁专门用来解决这种死锁现象

      临时用于快速解决线上项目发生阻塞死锁问题的

    from threading import RLock
    
    rlock = RLock()
    rlock.acquire()
    rlock.acquire()
    rlock.acquire()
    rlock.acquire()
    print(112233)
    rlock.release()
    rlock.release()
    rlock.release()
    rlock.release()
    
    print("程序结束 ... ")

    运行结果如下图所示

    4.用递归锁解决2中(面条-筷子)的死锁现象

    noodle_lock = kuaizi_lock = RLock()
    
    def eat1(name):
        noodle_lock.acquire()
        print("%s 抢到面条了" % (name))
        kuaizi_lock.acquire()
        print("%s 抢到筷子了" % (name))
    
        print("开始享受面条 ... ")
        time.sleep(0.5)
    
        kuaizi_lock.release()
        print("%s 放下筷子" % (name))
        noodle_lock.release()
        print("%s 放下面条" % (name))
    
    def eat2(name):
        kuaizi_lock.acquire()
        print("%s 抢到筷子了" % (name))
        noodle_lock.acquire()
        print("%s 抢到面条了" % (name))
    
        print("开始享受面条 ... ")
        time.sleep(0.5)
    
        noodle_lock.release()
        print("%s 放下面条" % (name))
        kuaizi_lock.release()
        print("%s 放下筷子" % (name))
    
    if __name__ == "__main__":
        name_lst1 = ["Fly","Hurt"]
        name_lst2 = ["Cat","Alan"]
    
        for name in name_lst1:
            Thread(target=eat1,args=(name,)).start()
    
        for name in name_lst2:
            Thread(target=eat2,args=(name,)).start()

    运行结果如下图所示

    5.用互斥锁解决2中(面条-筷子)的死锁现象

    用互斥锁解决问题,换句话来说,就是尽量用一把锁解决问题

    mylock = Lock()
    def eat1(name):
        mylock.acquire()
        print("%s 抢到面条了" % (name))
        print("%s 抢到筷子了" % (name))
        
        print("开始享受面条 ... ")
        time.sleep(0.5)    
    
        print("%s 放下筷子" % (name))
        print("%s 放下面条" % (name))
        mylock.release()
    
    def eat2(name):
        mylock.acquire()
        print("%s 抢到筷子了" % (name))
        print("%s 抢到面条了" % (name))
        
        print("开始享受面条 ... ")
        time.sleep(0.5)    
    
        print("%s 放下面条" % (name))    
        print("%s 放下筷子" % (name))
        mylock.release()
    
    if __name__ == "__main__":
        
        name_lst1 = ["A","B"]
        name_lst2 = ["C","D"]
        
        for name in name_lst1:
            Thread(target=eat1,args=(name,)).start()
            
        for name in name_lst2:
            Thread(target=eat2,args=(name,)).start()

    事件:Event

    e = Event()

    wait 动态添加阻塞

    clear 将内部的阻塞值改成False

    set 将内部的阻塞值改成True

    is_set 获取内部的阻塞值状态(True False)

    基本语法

    e = Event()
    print(e.is_set())
    e.set()
    print(e.is_set())
    e.clear()
    print(e.is_set())
    # 代表最多阻塞3秒
    e.wait(3)
    print("程序运行中... ")

    模拟链接远程数据库

    def check(e):
        # 用一些延迟来模拟检测的过程
        time.sleep(random.randrange(1,6)) # 1 2 3 4 5
        # time.sleep(1)
        print("开始检测链接用户的合法性")
        e.set()
        
        
    def connect(e):
        sign = False
        for i in range(1,4): # 1 2 3    
            # 设置最大等待1秒
            e.wait(1)    
        
            if e.is_set():
                print("数据库链接成功 ... ")
                sign = True
                break
            else:
                print("尝试链接数据库第%s次失败 ... " % (i))
                
        if sign == False:
            # 主动抛出异常,(超时异常)
            raise TimeoutError
            
    e = Event()
    # 线程1号负责执行连接任务
    Thread(target=connect,args=(e,)).start()
    
    # 线程2号负责执行检测任务
    Thread(target=check,args=(e,)).start()

    运行结果如下图所示

  • 相关阅读:
    Eclipse安装SVN插件(转载)
    推荐eclipse插件Properties Editor(转)
    Eclipse快捷键(转载)
    添加路由命令
    oracle 随机数(转载)
    《图解CSS3——第1章 揭开CSS3的面纱》
    css高度自适应
    JS生成随机数
    判断IE浏览器版本
    IE下SCRIPT5009: “JSON”未定义
  • 原文地址:https://www.cnblogs.com/libolun/p/13530797.html
Copyright © 2011-2022 走看看