线程&线程同步&死锁

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

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

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()

  

 
posted @ 2020-05-20 16:28  GumpYan  阅读(209)  评论(0编辑  收藏  举报