python 线程详解

1、什么是线程?

线程:操作系统提供的抽象概念,是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。

2、python如何实现多线程?

python通常使用threading模块实现多线程,导入threading包,然后对象名= threading.Thread(target = 函数名) 创建线程对象,最后对象名.start() 创建线程。主要的线程方法:run(): 用以表示线程活动的方法;start():启动线程活动;join([time]): 等待至线程中止。

https://docs.python.org/zh-cn/3.8/library/threading.html?highlight=threading#module-threading

3、实现多线程?

import threading
import time


def task1(s):
    task_list = ['file1', 'file2', 'file3']
    for i in task_list:
        print('正在下载:{}'.format(i))
        time.sleep(s)
        print('下载成功:{}'.format(i))


def task2(s):
    task_list = ['music1', 'music2', 'music3', 'music4']
    for i in task_list:
        print('正在听音乐:{}'.format(i))
        time.sleep(s)


if __name__ == '__main__':
    t1 = threading.Thread(target=task1, args=(1,))  # 创建线程1
    t2 = threading.Thread(target=task2, args=(0.5,))  # 创建线程2
    t1.start()  # 启动线程
    t2.start()

4、全局解释器锁?

全局解释器锁(GIL)只允许1个Python线程控制Python解释器。这也就意味着同一时间点只能有1个Python线程运行。如果你的Python程序只有一个线程,那么全局解释器锁可能对你的影响不大,但是如果你的程序是CPU密集型同时使用了多线程,那么程序运行可能会受到很大影响。

Python为了能够支持多线程,而解决多线程之间数据完整性和状态同步的最简单方法就是加锁,所以在python创立之初默认加上了GIL。

具体可以参考:https://zhuanlan.zhihu.com/p/361695757

但是在python当中,当计算强度不高时,GIL默认会加上,但是当计算强度比较大的时候,GIL又会释放,下面两个例子说明。

(1)当计算强度不高时,由于有锁,所以数据并不会出错

import threading
import time
"""
两个线程共享一个全局变量,
每个线程都对变量进行操作,输出线程结束后的变量的值
"""
tacket = 1000


def task1():
    global tacket
    for i in range(0, 150):
        tacket -= 1


def task2():
    global tacket
    for i in range(0, 160):
        tacket -= 1


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print(tacket)


结果:
690

(2)当计算强度高时,锁被释放,所以数据出错

import threading
import time
"""
两个线程共享一个全局变量,
每个线程都对变量进行操作,输出线程结束后的变量的值
"""
tacket = 10000000


def task1():
    global tacket
    for i in range(0, 1500000):
        tacket -= 1


def task2():
    global tacket
    for i in range(0, 1600000):
        tacket -= 1


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print(tacket)

结果:
7217607

5、线程同步(加锁)

多线程的可以共享主线程的数据,在操作的时候可能会存在数据不同的问题解决办法是,加锁,锁 是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对 Lock 对象加锁,线程在开始访问共享资源之前应先请求获得 Lock 对象。当对共享资源访问完成后,程序释放对 Lock 对象的锁定。

Python 的 threading 模块引入了锁(Lock)。threading 模块提供了 Lock 和 RLock 两个类,它们都提供了如下两个方法来加锁和释放锁:

  1. acquire(blocking=True, timeout=-1):请求对 Lock 或 RLock 加锁,其中 timeout 参数指定加锁多少秒。
  2. release():释放锁。

Lock 和 RLock 的区别如下:

  • threading.Lock:它是一个基本的锁对象,每次只能锁定一次,其余的锁请求,需等待锁释放后才能获取。
  • threading.RLock:它代表可重入锁(Reentrant Lock)。对于可重入锁,在同一个线程中可以对它进行多次锁定,也可以多次释放。如果使用 RLock,那么 acquire() 和 release() 方法必须成对出现。如果调用了 n 次 acquire() 加锁,则必须调用 n 次 release() 才能释放锁。

对线程进行加锁之后,计算结果就不会出错了 

import threading
import time
"""
两个线程共享一个全局变量,
每个线程都对变量进行操作,输出线程结束后的变量的值
"""
lock = threading.Lock()
tacket = 10000000


def task1():
    global tacket
    lock.acquire()
    for i in range(0, 1500000):
        tacket -= 1
    lock.release()


def task2():
    global tacket
    lock.acquire()
    for i in range(0, 1600000):
        tacket -= 1
    lock.release()


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print(tacket)

结果:
6900000

6、死锁

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源时,就会造成死锁。尽管死锁很少发生,但一旦发生就会造成应用的停止响应。

造成死锁的代码:

"""
开发过程中使用线程,在线程间共享多个资源的时候,
如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情。
"""
import threading
import time


lockA = threading.Lock()
lockB = threading.Lock()


def task1():
    if lockA.acquire():
        print('获取了A锁')  # 如果可以获取到锁则返回True
        time.sleep(0.5)
        if lockB.acquire():  # 如果锁被占用,等待
            print('又获取了B锁')
            lockB.release()
        lockA.release()


def task2():
    if lockB.acquire():
        print('获取了B锁')
        time.sleep(0.5)
        if lockA.acquire():
            print('又获取了A锁')
            lockA.release()
        lockB.release()


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)

    t1.start()
    t2.start()

利用超时时间避免死锁:

import threading
import time


lockA = threading.Lock()
lockB = threading.Lock()


def task1():
    if lockA.acquire(timeout=5):
        print('获取了A锁')  # 如果可以获取到锁则返回True
        time.sleep(0.5)
        if lockB.acquire(timeout=5):  # 如果锁被占用,等待
            print('又获取了B锁')
            lockB.release()
        lockA.release()


def task2():
    if lockB.acquire(timeout=5):
        print('获取了B锁')
        time.sleep(0.5)
        if lockA.acquire(timeout=5):
            print('又获取了A锁')
            lockA.release()
        lockB.release()


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)

    t1.start()
    t2.start()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楊木木8023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值