Python高级培训3——多线程


多线程

  • 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  • 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线 3.进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)
  • 及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;
  • 调度和切换:线程上下文切换比进程上下文切换要快得多

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  • 程序的运行速度可能加快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

多线程的三种基本状态

线程有 就绪、阻塞、运行 三种基本状态。

  1. 就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;
  2. 运行状态是指线程占有处理机正在运行;
  3. 阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。
    三种状态的相互转化如下图所示:
    三种状态的相互转化

创建多线程

import threading
import time


def test(x):
    print(x)
    time.sleep(2)


# t1 = threading.Thread(target=test, args=(1, ))
# t2 = threading.Thread(target=test, args=(2, ))
# t1.start()
# t2.start()

class MyThread(threading.Thread):
    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n

    def run(self):
        print("以类的方式创建多线程", self.n)
        time.sleep(2)


r1 = MyThread(11)
r2 = MyThread(22)
r1.start()
r2.start()

多线程的特性

"""
多线程的特性
"""
import threading
import time


def run(x):
    print(f"线程{x}\n")
    time.sleep(2)

# 1970年到现在过了多少秒
# print(time.time())


if __name__ == '__main__':
    start_time = time.time()
    # for i in range(50):
    #     t = threading.Thread(target=run, args=(i, ))
    #     t.start()
    # # start_time = time.time()
    # # run(1)
    # # run(2)
    # end_time = time.time()
    # print("两个run函数运行时间", end_time - start_time)
    # t1 = threading.Thread(target=run, args=(1, ))
    # t2 = threading.Thread(target=run, args=(2, ))
    # t1.start()
    # t1.join()  # 进入等待态,t1执行完后再执行t2
    # t2.start()

    res = []
    for i in range(50):
        t = threading.Thread(target=run, args=(i, ))
        t.setDaemon(True)  # 设置为守护线程,必须在start()方法之前设置,表示该线程是不重要的,进程退出时不需要等待这个线程执行完成。
        # 意义在于:避免子线程无限死循环,导致退不出程序,也就是避免楼上说的孤儿进程
        t.start()
        res.append(t)

    # 查看活动的线程个数
    print(f"线程个数{threading.active_count()}")

    # for t in res:
    #     t.join()

    print("两个run函数运行时间", time.time() - start_time)
    # t1 = threading.Thread(target=run, args=(1, ))
    # t2 = threading.Thread(target=run, args=(2, ))
    # t1.start()
    # t1.join()  # 进入等待态,t1执行完后再执行t2
    # t2.start()

    # 查看当前线程
    print(threading.current_thread())

线程锁

锁(Lock):一旦线程获得锁,其他试图获取锁的线程将被阻塞等待。
锁:凡是存在共享支援争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源。

名称含义
Lock.acquire(blocking=True,timeout=-1)获取锁,获取成功返回True,否则返回False当获取不到锁时,默认进入阻塞状态,直到获取到锁,后才继续。阻塞可以设置超时时间。非阻塞时,timeout禁止设置。如果超时依旧未获取到锁,返回False。
Lock.rease()释放锁,可以从任何线程调用释放。已上锁的锁,会被设置为unlocked。如果未上锁调用,会抛出RuntimeError异常。

加锁,解锁
一般来说,加锁就需要解锁,但是加锁后解锁前,还要有一些代码执行,就有可能抛异常,一旦出现异常,锁是无 法释放,但是当前线程可能因为这个异常被终止了,这也产生了死锁

加锁、解锁常用语句:

  • 使用try…finally语句保证锁的释放
  • with上下文管理,锁对象支持上下文管理

锁的应用场景:

  • 锁适用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。

使用锁注意事项:

  • 少用锁,必要时用锁。使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行。举例,高速公路上车并行跑,可是到了省界只开放了一个收费口,过了这个口,车辆依然可以在多车道 上一起跑。过收费口的时候,如果排队一辆辆过,加不加锁一样效率相当,但是一旦出现争抢,就必须 加锁一辆辆过。注意,不管加不加锁,只要是一辆辆过,效率就下降了。
  • 加锁时间越短越好,不需要就立即释放锁
  • 一定要避免死锁
"""
线程锁
"""
import threading

lock = threading.Lock()


def run():
    global x
    lock.acquire()
    x += 1
    lock.release()


if __name__ == '__main__':
    x = 0
    res = []

    for i in range(100):
        t = threading.Thread(target=run)
        t.start()
        res.append(t)

    for t in res:
        t.join()

    print(x)

递归锁

可重入锁,是线程相关的锁。
线程A获得可重复锁,并可以多次成功获取,不会阻塞。最后要在线程A中做和acquire次数相同的release。

"""
递归锁
"""
import threading


def run1():
    global x
    lock.acquire()
    x += 1
    lock.release()
    return x


def run2():
    global y
    lock.acquire()
    y += 1
    lock.release()
    return y


def run3():
    lock.acquire()
    res1 = run1()
    res2 = run2()
    lock.release()
    print(res1, res2)


if __name__ == '__main__':
    x = 0
    y = 0
    lock = threading.RLock()
    for i in range(50):
        t = threading.Thread(target=run3)
        t.start()

    while threading.active_count() != 1:
        print(f'正在运行{threading.active_count()}个线程')

    print("程序运行结束")

作业

在一个线程中,每秒循环输出当前的年月日时分秒;于此同时,
在另一个线程中,实现张三的姓名每2秒打印输出4次结束。注意:都需要使用类和继承实现功能。

import threading
import time


class TwoThread(threading.Thread):  # 继承Thread类
    def __init__(self, type):  # 构造器,初始化变量,type表示下面要执行的函数
        super().__init__()
        self.type = type

    def run(self):  # 重写run方法
        if self.type == 1:  # 如果type == 1,循环打印当前时间
            self.cur_time()
        else:  # 否则,输出4次张三姓名
            self.get_Name()

    def cur_time(self):
        while True:  # 一直循环
            print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))  # 输出当前时间
            time.sleep(1)  # 等待1S

    def get_Name(self):
        for i in range(4):  # 循环4次
            print("姓名:张三")
            time.sleep(2)  # 等待2S


if __name__ == '__main__':
    tt1 = TwoThread(1)  # 实例化对象
    tt2 = TwoThread(2)  # 实例化对象
    tt1.start()  # 用于启动线程的活动。start()方法在内部调用run()方法,然后执行目标方法。
    tt2.start()  # 用于启动线程的活动。start()方法在内部调用run()方法,然后执行目标方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值