zoukankan      html  css  js  c++  java
  • 线程&线程同步&死锁

    并发就是多线程轮流使用同一进程的资源

    进程里面可以存着多个线程,多个线程共用进程资源,因此存在多个线程争夺进程资源的问题

    1. 线程状态:

    新建,就绪,运行,阻塞,结束

    阻塞完之后,会回到就绪上来

     

    import threading
    from time import sleep
    
    
    def download(n):
        images = ['1.jpg', '2.jpg', '3.jpg']
        for image in images:
            print('正在下载:', image)
            sleep(n)
            print('下载{}成功!:'.format(image))
    
    
    def listen_music(n):
        musics = ['大碗宽面', '土耳其冰淇淋', '烤面筋', '人民广场炸鸡']
        for music in musics:
            sleep(n)
            print('正在听{}歌!'.format(music))
    
    
    if __name__ == '__main__':
        # 创建线程对象
        t = threading.Thread(target=download, name='aaa', args=(1, ))
        t.start()
    
        t1 = threading.Thread(target=listen_music, name='bbb', args=(0.5, ))
        t1.start()
    

      

     线程是可以共享全局变量

    import threading
    
    
    money = 1000
    
    
    def run1():
        global money
        for i in range(100):
            money -= 1
    
    
    if __name__ == '__main__':
        th1 = threading.Thread(target=run1)
        th2 = threading.Thread(target=run1)
        th3 = threading.Thread(target=run1)
        th4 = threading.Thread(target=run1)
    
        th1.start()
        th2.start()
        th3.start()
        th4.start()
    
        th1.join()
        th2.join()
        th3.join()
        th4.join()
    
        print('money:', money)
    

      输出:

     2.线程同步

    GIL(Global Interpreter Lock)全局解释器锁

    先明确一个问题,Python中的多线程是假的多线程! 为什么这么说,我们先明确一个概念,全局解释器锁(GIL)。

    Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,只有一个线程在解释器中运行。

    对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行。

    1.设置GIL。

    2.切换到一个线程去执行。

    3.运行。

    4.把线程设置为睡眠状态。

    5.解锁GIL。

    6.再次重复以上步骤。

    引子:

    import threading
    
    n = 0
    
    
    def task1():
        global n
        for i in range(1000000):
            n += 1
        print('-----> task1中的n值是:', n)
    
    
    def task2():
        global n
        for i in range(1000000):
            n += 1
        print('-----> task2中的n值是:', n)
    
    
    if __name__ == '__main__':
        th1 = threading.Thread(target=task1)
        th2 = threading.Thread(target=task2)
    
        th1.start()
        th2.start()
    
        th1.join()
        th2.join()
    
        print('n:', n)
    

      

     这个问题,就是线程异步的问题,CPU轮流执行各个线程出现的问题,因此需要一个锁,即线程同步(出现执行速度慢的问题)

    只要有共享数据,就要使用线程同步

    Python底层只要用线程,默认加锁(GIL),只要有锁,就没办法达到真正的多线程

    Python底层针对只要运算量达到某个程度,就自动释放GIL,因此出现的n最后的值不是2百万

    在数据共享方面进程与线程的区别:

    进程是每个进程中都有一份

    线程是共用一个数据 ---》数据安全性问题

    进程:一般使用于计算密集型(计算量大的,快速计算的)

    线程:一般耗时操作的时候用,如下载,爬虫,IO操作

    如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性(数据安全),需要对多个线程进行 同步。
    同步:一个一个的完成,一个做完另一个才能进来
    效率就会降低
    使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有aquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间
    多线程的优势在于可以同时运行多个任务(至少感觉起来是这样),但当多线程需要共享数据时,可能存在数据不同步的问题,为了避免这种情况,引入锁的概念
     
    import threading
    import time
    
    lock = threading.Lock()
    lyst = [0] * 10
    
    
    def task1():
        # 获取线程锁,如果已经加上锁,则等待锁的释放
        lock.acquire()   # 阻塞
        for i in range(len(lyst)):
            lyst[i] = 1
            time.sleep(0.5)
        lock.release()   # 释放锁,只要不释放,其他的线程都无法进入运行状态
    
    
    def task2():
        lock.acquire()  # 请求得到锁
        for i in range(len(lyst)):
            print('---->', lyst[i])
            time.sleep(0.5)
        lock.release()
    
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=task1)
        t2 = threading.Thread(target=task2)
    
        t2.start()
        t1.start()
    
        t2.join()
        t1.join()
    
        print(lyst)
    

      

    3.死锁

     开发过程中使用线程,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源,并且同时等待对方的资源,就会造成死锁。

    尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情

     一定要避免出现死锁,解决方式:

    1.重构代码

    2.在acquire(timeout=5)加入timeout,只要超时,就将锁释放

    from threading import Thread, Lock
    import time
    
    lockA = Lock()
    lockB = Lock()
    
    
    class MyThread(Thread):
        def run(self):  # start
            if lockA.acquire():  # 如果可以获取到锁则返回True
                print(self.name+'获取了A锁')  # 系统默认name时Thread-1
                time.sleep(0.1)
                if lockB.acquire():  # B锁已经被thread1拿了,因此就会阻塞
                    print(self.name+'又获取了B锁,原来还有A锁')
                    lockB.release()
                lockA.release()
    
    
    class MyThread1(Thread):
        def run(self):  # start
            if lockB.acquire():  # 如果可以获取到锁则返回True
                print(self.name+'获取了B锁')  # 系统默认name时Thread-2
                time.sleep(0.1)
                if lockA.acquire():
                    print(self.name+'又获取了A锁,原来还有B锁')
                    lockA.release()
                lockB.release()
    
    
    if __name__ == '__main__':
        th = MyThread()
        th1 = MyThread1()
    
        th.start()
        th1.start()
    

      

     解决

    from threading import Thread, Lock
    import time
    
    lockA = Lock()
    lockB = Lock()
    
    
    class MyThread(Thread):
        def run(self):  # start
            if lockA.acquire():  # 如果可以获取到锁则返回True
                print(self.name+'获取了A锁')  # 系统默认name时Thread-1
                time.sleep(0.1)
                if lockB.acquire(timeout=3):  # B锁已经被thread1拿了,因此就会阻塞
                    print(self.name+'又获取了B锁,原来还有A锁')
                    lockB.release()
                lockA.release()
    
    
    class MyThread1(Thread):
        def run(self):  # start
            if lockB.acquire():  # 如果可以获取到锁则返回True
                print(self.name+'获取了B锁')  # 系统默认name时Thread-2
                time.sleep(0.1)
                if lockA.acquire(timeout=3):
                    print(self.name+'又获取了A锁,原来还有B锁')
                    lockA.release()
                lockB.release()
    
    
    if __name__ == '__main__':
        th = MyThread()
        th1 = MyThread1()
    
        th.start()
        th1.start()
    

      

     
  • 相关阅读:
    [GIT] warning: LF will be replaced by CRLF问题解决方法
    最近想学的工具
    如何在webstrom中配置eslint和less
    Git常用命令
    windows下nginx安装、配置与使用
    关于 addEventListener 和 handleEvent 方法
    PHP中的魔术方法总结 :__construct, __destruct , __call, __callStatic,__get, __set, __isset, __unset , __sleep, __wakeup, __toStr
    Git使用详细教程
    9个永恒的UI设计原则
    常见浏览器兼容性问题与解决方案
  • 原文地址:https://www.cnblogs.com/GumpYan/p/12924393.html
Copyright © 2011-2022 走看看