07 - 多任务-线程

目录

1. 多任务-线程基本介绍
  • 多任务的理解
  • 线程完成多任务
2. 锁的引入与应用
  • 互斥锁与死锁的引入
  • 线程同步 案例

课堂笔记

1. 多任务-线程基本介绍
1.1 多任务的理解
  • 多任务,即我们所说的一心多用,如边开车时边听音乐…
  • 程序中模拟多任务
import threading

def drive():
    for i in range(3):
        print('正在开车....')

def music():
    for i in range(3):
        print('正在听音乐....')
        
if __name__ == '__main__':
    drive()
    music()
1.2 线程完成多任务
  • 多线程:程序中独立运行的片段称为线程,而软件或硬件上实现多个线程并发执行就称为多线程。
  • 线程执行流程:主线程会等待子线程执行完毕后再执行,而子线程间会进行资源争夺,谁先抢到资源谁先执行。
  • 使用Thread类时并不会创建线程,只有再start()方法调用时才会创建并启动线程。
import threading

def drive():
    for i in range(3):
        print('正在开车....')

def music():
    for i in range(3):
        print('正在听音乐....')
# 定义线程测试函数
def threadTest():
    # 指定线程执行目标以及线程名,此时并不创建线程
    t1 = threading.Thread(target=drive, name='thread1')
    # 创建并执行线程
    t1.start()
    t2 = threading.Thread(target=music, name='thread2')
    t2.start()

if __name__ == '__main__':
    threadTest()
  • 继承Thread类创建线程 内部必须重写run方法
class Test(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        for i in range(3):
            print(i)

if __name__ == '__main__':
    t = Test('test_name')
    t.start()
    # 获取当前线程信息
    print(threading.enumerate())
    # [<_MainThread(MainThread, started 12404)>, <Test(test_name, started 5540)>]
  • join()与setDaemon()区别

    • jion():守护子线程,主线程A中调用子线程B,并调用B.join()方法,主线程会一直等待直到子线程结束然后返回调用的地方继续执行,join()方法可设置一个时间参数,用于控制最大等待时间,若超过该时间,线程会自动返回按正常方式执行
    • setDaemon():守护线程,主线程A中调用子线程B,并在主线程中调用setDaemon()方法时,子线程会随着主线程的结束而结束,而不必等待子线程结束
    class Test(threading.Thread):
        def __init__(self, name):
            super().__init__(name=name)
    
        def run(self):
            for i in range(3):
                print(i)
    
    if __name__ == '__main__':
        t = Test('test_name')
        t.setDaemon(True)
        t.start()
        print('主线程结束')
    '''
    输出:
    0
    主线程结束
    '''
    
  • 线程间共享全局变量:在一个函数中,对全局变量进行修改的时候,是否要加global要看是否对全局变量的指向进行了修改,如果修改了指向,那么必须使用global,仅仅是修改了指向的空间中的数据,此时不用必须使用global。

def drive():
    # 修改全局变量指向
    global x
    x += 1
    print(x)
    print('正在开车....')

def music():
    # 仅修改值
    x = 5
    print(x)
    print('正在听音乐....')

def threadTest():
    t1 = threading.Thread(target=drive, name='thread1')
    t1.start()
    print(x)
    t2 = threading.Thread(target=music, name='thread2')
    t2.start()

if __name__ == '__main__':
    x = 10
    threadTest()
'''
输出:
11
正在开车....
11
5
正在听音乐....
'''
2. 锁的引入与应用
2.1 互斥锁与死锁的引入
  • 为什么会有锁

    • 多线程情况下,会面对几个线程同时对数据的读取与写入的操作,这就可能造成数据来不及修改就被其他线程读取,这显然不是真实的数据,而是修改前的数据,从而会造成错误。
  • 互斥锁:当多个线程同时共享某数据时,需要进行同步控制,即锁定该数据当前为线程所有,直到该线程释放锁定后,才能对其他线程共享。互斥锁保证每次只能被一个线程进行写入操作,从而保证了多数据情况下数据的准确性。

    • 创建锁:mutex = threading.Lock() 或 threading.RLock()
      • threading.Lock()只能创建独立的锁,即相同锁之间不能嵌套。
      • threading.RLock()可创建多个锁,锁之间可以嵌套。
    • 上锁:mutex.acquire()
    • 解锁:mutex.release()
  • 死锁:线程间对资源互相等待的关系,即两个线程各自拥有部分上锁资源,而又同时在等待对方的资源,如此就呈现出死循环状态。

    • 避免方法:设置锁定超时时间 mutex.acquire(timeout)
    import time, threading
    
    # 创建互斥锁mutex_A、mutex_B
    mutex_A = threading.Lock()
    mutex_B = threading.Lock()
    
    class MyThread1(threading.Thread):
        def run(self) -> None:
            # 对mutex_A上锁
            mutex_A.acquire()
            print('__mutex_A_start_')
            # 对mutex_A上锁后,等待0.1s 此时另外一个线程就会对mutex_B上锁
            time.sleep(0.1)
    
            # 对mutex_B上锁
            mutex_B.acquire()
            print('__mutex_A_B__')
            # 对mutex_B解锁
            mutex_B.release()
    
            print('__mutex_A_end__')
            # 对mutex_A解锁
            mutex_A.release()
    
    class MyThread2(threading.Thread):
        def run(self) -> None:
            # 对mutex_B上锁
            mutex_B.acquire()
            print('__mutex_B_start_')
            # 对mutex_B上锁后,等待0.1s 此时另外一个线程就会对mutex_A上锁
            time.sleep(0.1)
    
            # 对mutex_A上锁
            mutex_A.acquire()
            print('__mutex_B_A__')
            # 对mutex_A解锁
            mutex_A.release()
    
            print('__mutex_B_end__')
            # 对mutex_B解锁
            mutex_B.release()
    
    if __name__ == '__main__':
        t1 = MyThread1()
        t2 = MyThread2()
    
        t1.start()
        t2.start()
    
2.2 线程同步 案例
  • 目标:

    B: A同学
    A: 在
    B: 现在几点了?
    A: 你猜猜现在几点了。
    
  • threading.Condition()实现

    class StuA(threading.Thread):
        def __init__(self, cond):
            super().__init__(name='A')
            self.cond = cond
    
        def run(self) -> None:
            # 相当于上锁
            with self.cond:
                self.cond.wait()        # 等待其他线程
                print('{}: 在'.format(self.name))
                self.cond.notify()      # 唤醒B
                self.cond.wait()        # 等待B
                print('{}: 你猜猜现在几点了。'.format(self.name))
                self.cond.notify()
    
    
    class StuB(threading.Thread):
        def __init__(self, cond):
            super().__init__(name='B')
            self.cond = cond
    
        def run(self) -> None:
            # 相当于上锁
            with self.cond:
                print('{}: A同学'.format(self.name))
                self.cond.notify()      # 唤醒A
                self.cond.wait()        # 等待A
                print('{}: 现在几点了?'.format(self.name))
                self.cond.notify()
    
    
    if __name__ == '__main__':
        cond = threading.Condition()
        s1 = StuA(cond)
        s2 = StuB(cond)
    
        s1.start()
        s2.start()
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值