• Python线程编程之Thread详解


    一、线程编程(Thread)

    1、线程基本概念

    1.1、什么事线程

    • 线程被称为轻量级的进程
    • 线程也可以使用计算机多核资源,是多任务编程方式
    • 线程是系统分配内核的最小单元
    • 线程可以理解为进程的分支任务

    1.2、线程特征

    • 一个进程中可以包含多个线程
    • 线程也是一个运行行为,消耗计算机资源
    • 一个线程中的所有线程共享这个进程的资源
    • 多个线程之间的运行互不影响各自运行
    • 线程的创建和销毁消耗资源远小于进程
    • 各个线程也有自己的ID等特征

    二、threading模块创建线程

    1、创建线程对象

    1
    2
    3
    4
    5
    6
    from threading import Thread
    t = Thread()
    功能: 创建线程对象
    参数: target 绑定线程函数
        args 元组 给线程函数位置传参
        kwargs 字典 给线程函数键值传参

    2、 启动线程

    1
    t.start()

    3、 回收线程

    t.join([timeout])

    4、代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    """
    thread1.py 线程基础使用
    步骤:
    1. 封装线程函数
    2.创建线程对象
    3.启动线程
    4.回收线程
    """
    import os
    from threading import Thread
    from time import sleep
    a = 1
    # 线程函数
    def music():
        for i in range(3):
            sleep(2)
            print('播放:黄河大合唱 %s' % os.getpid())
        global  a
        print("a,",a)
        a = 1000
    # 创建线程对象
    t = Thread(target=music)
    # 启动线程
    t.start()
    for i in range(3):
        sleep(1)
        print('播放:beauty love %s' % os.getpid())
    # 回收线程
    t.join()
    print('程序结束')
    print("a,", a)

    5、线程对象属性

    1.t.name 线程名称

    2.t.setName() 设置线程名称

    3.t.getName()获取线程名称

    4.t.is_alive() 查看线程是否在生命周期

    5.t.daemon 设置主线程和分支线程退出分支线程也退出.要在start前设置 通常不和join 一起使用

    6.代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    """
    thread3.py
    线程属性演示
    """
    from threading import Thread
    from time import sleep
     
    def fun():
        sleep(3)
        print('线程属性测试')
     
    t = Thread(target=fun, name='ceshi')
    # 主线程退出分支线程也退出 必须在start前使用 与join 没有意义
    t.setDaemon(True)
    t.start()
    print(t.getName())
    t.setName('Tedu')
    print('is alive:', t.is_alive())
    print('daemon', t.daemon)

    6、自定义线程类

    1.创建步骤

    1.继承Thread类

    2.重写 __init__方法添加自己的属性 使用super加载父类属性

    3.重写run方法

    2.使用方法

    1.实例化对象

    2.调佣start自动执行run方法

    3.调佣join回收线程

    代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    """
    自定义线程类例子
    """
    from threading import Thread
     
    # 自定义线程类
    class ThreadClass(Thread):
        # 重写父类 init
        def __init__(self, *args, **kwargs):
            self.attr = args[0]
            # 加载父类init
            super().__init__()
        # 假设需要很多步骤完成功能
        def f1(self):
            print('1')
        def f2(self):
            print(2)
        # 重写run 逻辑调佣
        def run(self):
            self.f1()
            self.f2()
     
    t = ThreadClass()
    t.start()
    t.join()

    7、一个很重要的练习 我很多不懂

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from threading import Thread
    from time import sleep, ctime
     
    class MyThread(Thread):
        def __init__(self, group=None, target=None, name=None,
                     args=(), kwargs=None, *, daemon=None):
            super().__init__()
            self.fun = target
            self.args = args
            self.kwargs = kwargs
        def run(self):
            self.fun(*self.args, **self.kwargs)
     
    def player(sec, song):
        for i in range(3):
            print("Playing %s : %s" % (song, ctime()))
            sleep(sec)
     
    t = MyThread(target=player, args=(3,), kwargs={'song': '量量'})
    t.start()
    t.join()

    Python客栈送红包、纸质书

    8、线程间通信

    1.通信方法

    1.线程间使用全局遍历进行通信

    2.共享资源争夺

    1.共享资源:多个进程或者线程都可以操作的资源称为共享资源,对共享资源的操作代码段称为临界区

    2.影响:对公共资源的无序操作可能会带来数据的混乱,或者操作错误.此时往往需要同步互斥机制协调操作顺序

    3.同步互斥机制

    1.同步:同步是一种协作关系,为完成操作,多进程或者线程形成一种协调,按照必要的步骤有序执行操作

    ​[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CDK1Js97-1639582357964)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211215223825231.png)]

    ​ 2.互斥:互斥是一种制约关系,当一个进程或者线程占有资源时,会进行加锁处理,此时其它进程线程就无法操作该资源,直到解锁后才能操作

    1
    ## 9.线程同步互斥方法

    1. 线程Event 代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from threading import Event
    # 创建线程event对象
    e = Event()
    # 阻塞等待e被set
    e.wait([timeout])
    # 设置e, 使wait结束阻塞
    e.set()
    # 使e回到未被设置状态
    e.clear()
    # 查看当前e是否被设置
    e.is_set()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    """
    event 线程互斥方法演示
    """
    from threading import Event, Thread
    s = None  # 用于通信
    e = Event()
    def yzr():
        print('杨子荣前来拜山头')
        global s
        s = '天王盖地虎'
        e.set() #操作完共享资源 e设置
     
    t = Thread(target=yzr)
    t.start()
    print('说对口令就是自己人')
    e.wait() #阻塞等待 e.set()
    if s == '天王盖地虎':
        print('宝塔镇河妖')
        print('确认过眼神,你是对的人')
        e.clear()
    else:
        print('打死他...')
    t.join()
    print('程序结束')

    2. 线程锁 Lock代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    from threading import Lock
    lock = Lock()创建锁对象
    lock.acquire() 上锁 如果lock已经上锁再调用会阻塞
    lock.release() 解锁
    with lock: 上锁
    ....
    ....
    with 代码块解锁自动解锁
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    """
    thread_lock
    线程锁演示
    """
    from threading import Thread, Lock
    a = b = 0
    lock = Lock()
     
    def value():
        while True:
            # 上锁
            lock.acquire()
            print('a=%d,b=%d' % (a, b)) if a != b else print('a不等于b')
            # 解锁
            lock.release()
     
    t = Thread(target=value)
    t.start()
    while True:
        # with 开始上锁
        with lock:
            a += 1
            b += 1
        # with 解锁 自动解锁
    t.join()
    print('程序结束')

    10、死锁及其处理

    1.定义

    死锁是指两个或者两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,他们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁.

    2.图解

    3. 死锁产生条件

    死锁发生的必要条件

    • 互斥条件:指线程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    • 请求和保持条件:指线程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求线程阻塞,但又对自己已获得的其它资源保持不放。
    • 不剥夺条件:指线程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放,通常CPU内存资源是可以被系统强行调配剥夺的。
    • 环路等待条件:指在发生死锁时,必然存在一个线程——资源的环形链,即进程集合{T0,T1,T2,···,Tn}中的T0正在等待一个T1占用的资源;T1正在等待T2占用的资源,……,Tn正在等待已被T0占用的资源。
    • 死锁的产生原因

    简单来说造成死锁的原因可以概括成三句话:

    • 当前线程拥有其他线程需要的资源
    • 当前线程等待其他线程已拥有的资源
    • 都不放弃自己拥有的资源

    如何避免死锁

    死锁是我们非常不愿意看到的一种现象,我们要尽可能避免死锁的情况发生。通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率。

    4.死锁代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    from time import sleep
    from threading import Thread, Lock
     
    # 交易类
    class Account:
        def __init__(self, _id, balance, lock):
            # 用户
            self._id = _id
            # 存款
            self.balance = balance
            # 锁
            self.lock = lock
        # 取钱
        def withdraw(self, amount):
            self.balance -= amount
        # 存钱
        def deposit(self, amount):
            self.balance += amount
        # 余额
        def get_balance(self):
            return self.balance
     
    Tom = Account('Tom', 5000, Lock())
    Alex = Account('Alex', 8000, Lock())
     
    def transfer(from_, to, amount):
        # 锁住自己账户
        if from_.lock.acquire():
            # 账户减少
            from_.withdraw(amount)
            sleep(0.5)
            if to.lock.acquire():
                to.deposit(amount)
                to.lock.release()
            from_.lock.release()
        print('转账完成 %s给%s转账%d' % (from_._id, to._id, amount))
     
    # transfer(Tom, Alex, 1000)
    t1 = Thread(target=transfer, args=(Tom, Alex, 2000))
    t2 = Thread(target=transfer, args=(Alex, Tom, 3500))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('程序结束')

    python线程GIL

    1.python线程的GIL问题 (全局解释器锁)

    什么是GIL :由于python解释器设计中加入了解释器锁,导致python解释器同一时刻只能解释执行一个线程,大大降低了线程的执行效率。

    导致后果: 因为遇到阻塞时线程会主动让出解释器,去解释其他线程。所以python多线程在执行多阻塞高延迟IO时可以提升程序效率,其他情况并不能对效率有所提升。

    GIL问题建议

    • 尽量使用进程完成无阻塞的并发行为
    • 不使用c作为解释器 (Java C#)

    总结:

    在无阻塞状态下,多线程程序和单线程程序执行效率几乎差不多,甚至还不如单线程效率。但是多进程运行相同内容却可以有明显的效率提升。

  • 相关阅读:
    使用JDBC连接MySql时出现:The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration
    Mysql Lost connection to MySQL server at ‘reading initial communication packet', system error: 0
    mysql-基本命令
    C# 监听值的变化
    DataGrid样式
    C# 获取当前日期时间
    C# 中生成随机数
    递归和迭代
    PHP 时间转几分几秒
    PHP 根据整数ID,生成唯一字符串
  • 原文地址:https://www.cnblogs.com/xiongwei/p/16458228.html
Copyright © 2020-2023  润新知