python多线程详解(一)

python多线程详解(一)

全局解释器锁(GIL)

​ GIL是Python的一个历史遗留问题,它使同一时间只能有一个线程在使用解释器。

​ 这样做的好处是,避免资源竞争,保证线程安全。但这样做同样会带来一定的问题,那就是性能低下,这也是python为什么这么慢的原因之一。

创建线程

​ 每运行一个程序,就会创建一个进程,在创建一个进程的同时也会创建一个线程(主线程),因为线程是计算机执行任务的最小单位,一个进程中可以包含多个线程,其他的线程都是由主线程创建的。

​ python通过自带的threading模块来实现对多线程的支持,有两种方法来创建线程,

  1. 通过函数来创建线程。
import threading

def one(a, b):
    print(a + b) 
    
one = threading.Thread(target=one, args=(2, 3))
one.start()

'''
5
'''

target参数用于传入函数对象。args参数用于以元组的形式传入参数。也可以通过kwargs参以字典的形式传入关键字参数。还可以通过name参数来设置线程的名字。

  1. 通过继承threading.Thread类来创建线程。
from threading import Thread

class One(Thread):
    def run(self):
        print("线程1")
        
one = One()
one.start()

'''
线程1
'''

​ 继承了Thread类之后,通过重写run()方法,将需要执行的任务放在run方法中,但是不可以重写Thread类中的其他方法,否则会报错。

线程对象

​ 线程对象具有以下的属性和方法:

属性/方法描述
name线程对象的名称,初始名称可以在构造函数中设置,也可以直接修改属性值。
native_ident当前线程在系统中的“线程标识符”,是一个非负整数,如线程未启动则返回值为None。(3.8新功能)
ident当前线程的“线程标识符”,是一个非零整数,如线程未启动则返回值None
daemon表示这个线程是否是守护线程的布尔值。
start()启动线程的方法
is_alive()判断线程是否存活
join(timeout=None)等待,直到主线程结束。

守护线程和join()

​ 默认情况下,在创建多个线程时,当主线程的任务执行完时,主线程会直接结束,而其他的线程则会继续执行,直到结束。

​ 守护线程,直接设置daemon属性,将线程设置为守护线程。守护线程会和主线程保持同步,即主线程什么时候结束,守护线程什么时候结束,不管守护线程是否运行结束。

​ join()方法,对线程使用join()方法,会让主线程阻塞等待调用了join()方法的线程。

设置守护线程需要在主线程启动前调用,调用join()方法就需要在主线程启动之后调用

线程安全问题

​ 多线程虽然可以并发的执行任务,提高程序的运行效率,但是这样同样会带来线程安全问题。

​ 因为同一进程中的线程,共享全局资源,所以当同时有多个线程对同一资源进行操作时,就会产生冲突,问题也就随之而来了。

import threading

a = 0


def one():
    global a
    for i in range(1000000):
        a += 1
    return


def two():
    global a
    for j in range(1000000):
        a += 1
    return


one = threading.Thread(target=one)
two = threading.Thread(target=two)
one.start()
two.start()
print(a)

'''
508036
'''

​ 按照预计,最后的结果应该是2000000,但实际结果区却并不是这样,这就是因为当有多个线程同时对全局变量a进行操作时,就会产生冲突,由此就会引发线程安全问题。

线程锁

同步锁(threading.Lock)

​ 线程锁的出现就是为了解决多个线程之间的冲突,它使得同一时间只能有一个线程对加了锁的资源进行操作。这样可以在一定程度上解决线程中的冲突问题,因为对资源加了锁,使得多线程在计算密集型的任务中优势不再明显,不过在I/O密集型任务中多线程的优势还是非常明显的。

​ 资源加锁后,只有获得了锁的线程才能够对资源进行操作,未获得锁的线程不能对资源进行操作,只能等待获得锁的线程释放锁后才能获得锁,然后对资源进行操作。

​ 锁的使用也非常简单,从threading模块中导入Lock类,然后实例化这个类,得到一个锁对象,然后在可能会出现线程安全的地方,获得锁,释放锁。

import threading
from threading import Lock
import time

# 实例化锁
lock = Lock()
a = 0


def one():
    global a
    for i in range(1000000):
        # 获得锁
        lock.acquire()
        a += 1
        # 释放锁
        lock.release()
    return


def two():
    global a
    for j in range(1000000):
        lock.acquire()
        a += 1
        lock.release()
    return


one = threading.Thread(target=one)
two = threading.Thread(target=two)

one.start()
two.start()
one.join()
two.join()
print(a)

'''
2000000
'''

死锁

​ 不过线程锁,并不是万能的,如果过度的使用同步锁,就会出现问题。当两个线程相互等待时,就会出现死锁。就像下面的情况:

import threading
from threading import Lock

lock1 = Lock()
lock2 = Lock()
a = 0


def one():
    global a
    for i in range(1000000):
        lock1.acquire()
        lock2.acquire()
        a += 1
        lock1.release()
        lock2.release()
    return


def two():
    global a
    for j in range(1000000):
        lock2.acquire()
        lock1.acquire()
        a += 1
        lock2.release()
        lock1.release()
    return


one = threading.Thread(target=one)
two = threading.Thread(target=two)

one.start()
two.start()
one.join()
two.join()
print(a)

递归锁(threading.Rlock)

​ 递归锁也叫可重入锁,它可以在死锁的情况下,使一个线程再次获得锁,从而打破死锁的情况。不过递归锁并不是万能的,在某些情况下仍然会死锁。

​ 递归锁的用法和同步锁有一点小区别,修改上面的代码,就可以解决死锁。

import threading
from threading import Lock, RLock

lock1 = lock2 = RLock()
a = 0


def one():
    global a
    for i in range(1000000):
        lock1.acquire()
        lock2.acquire()
        a += 1
        lock1.release()
        lock2.release()
    return


def two():
    global a
    for j in range(1000000):
        lock2.acquire()
        lock1.acquire()
        a += 1
        lock2.release()
        lock1.release()
    return

'''
2000000
'''
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值