当前位置:   article > 正文

python--实验14 并发编程(多线程)

python--实验14 并发编程(多线程)

知识点 

 

  • 1 并发编程

    • 1.1程序提速手段
    • 1.2多任务
      • 并发

        • 在一个CPU一段时间内交替去执行任务。在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”。

      • 并行

        • 对于多核CPU处理多任务,操作系统会给CPU的每个内核安排一个执行的软件,多个内核分别运行不同的程序。

    • 1.3进程
      • 进程定义

        • 一个正在运行的程序或者软件就是一个进程。每个进程都有自己独立的一块内存空间。

      • 静态--程序 动态--进程是“活着”的程序 地址空间、内存、数据栈、用于跟踪执行的辅助程序

    • 1.4线程
      • 线程定义

        • 线程是进程中执行代码的一个分支,是CPU调度和分派的基本单位,一个进程可以有一个或多个线程,各个线程之间共享进程的内存空间。程序中的线程在相同的内存空间中执行,并共享许多相同的资源。

      • 线程特点

    • 1.5python对并发编程的支持
      • 多线程

        • threading,利用CPU和IO可以同时执行的原理

      • 多进程

        • multiprocessing,利用多核CPU的能力,真正的并行执行任务。

      • 异步IO

        • asyncio,利用单线程CPU和IO可以同时执行的原理,实现函数异步执行。

      • 注意

        • 利用Lock对资源加锁,防止冲突访问。

        • 使用Queue实现不同线程/进程之间的数据通信,实现生产者-消费者模式

        • 使用线程池Pool/进程池Pool,简化任务提交、等待结果、获取结果

        • 使用subprocess启动外部程序的进程,并进行输入输出交互

    • 1.6全局解释器锁GIL
      • 定义

      • I/O密集型计算采用多线程的方法

    • 1.7根据任务选择并发技术
  • 2 多进程

    • 2.1四种创建方式
      • os.fork()函数

      • multiprocessing模块Process类创建进程

        • Python的多进程包multiprocessing可以完成从单进程到并发执行的转换,支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Lock等组件。

        • 创建进程

          • Process类,该类可用来在Windows平台上创建新进程。

          • 直接创建Process类的实例对象

            • (1)导入进程包

            • (2)Process进程类的说明

          • 通过继承Process类的子类,创建实例对象。

          • 注意,继承Process类的子类需重写父类的run()方法。

      • Process子类创建进程

      • 进程池Pool类创建进程

        • 常用方法

    • 2.2进程间通信
      • 常用方法

        • 管道是双向通信,数据进程不安全,队列是管道加锁来实现的

      • queue类

  • 3 多线程

    • 线程

      • 进程相当于房子,有(地址空间、内存、数据栈等)浴室、厨房; 线程相当于房子里的居住者,可以使用浴室洗澡,厨房做饭等。

    • 3.1创建线程
      • threading模块

        • 概述

          • Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。

          • Python标准库模块threading提供了与线程相关的操作:创建线程、启动线程、线程同步。

          • 通过创建threading.Thread对象实例,可以创建线程;调用Thread对象的start()方法,可启动线程。也可以创建Thread的派生类,重写run方法,然后创建其对象实例来创建线程。

        • 函数样式

        • 常用方法

        • 示例

      • 说明

    • 3.2线程加入join()
      • 语法

        • Thread对象.join()

      • 作用

        • 若采用 t.join(),即让包含代码的线程(tc,即当前线程)“加入”到另外一个线程(t)的尾部。在线程(t)执行完毕之前,线程(tc)不能执行。

      • 说明

        • 1. 线程不能加入自己,否则将导致Runtime Error,形成死锁。

          • 2. 线程不能加入未启动的线程,否则将导致Runtime Error。

      • 示例

    • 3.3自定义派生于Thread的对象
      • 定义

        • 通过声明Thread的派生类,创建一个子类,并重写对象的run方法,然后创建其对象实例,可创建线程。通过对象的start方法,可启动线程,并自动执行对象的run方法。

      • 注意

        • 子类的构造器一定要先调用基类的构造器

        • 直接将线程需要做的事写到run方法中

      • 示例

  • 4 锁

    • 4.1锁--线程安全
      • 定义

      • 两种状态

      • 两种模式

    • 线程安全示例

      • 背景

        • 创建工作线程,模拟银行现金帐户取款。多个线程同时执行取款操作时,如果不使用同步处理,会造成账户余额混乱;尝试使用同步锁对象Lock,以保证多个线程同时执行取款操作时,银行现金帐户取款的有效和一致。

      • 代码

  • 5 生产者-消费者模型

    • 5.1生产者和消费者模型
    • 5.2queue模块
      • 两种通信

      • 概念

        • 使用Python标准模块queue提供了适用于多线程编程的先进先出的数据结构(即队列),用来在生产者和消费者线程之间的信息传递。使用queue模块中的线程安全的队列,可以快捷实现生产者和消费者模型。

      • 主要方法

        • (1)Queue(maxsize=0):构造函数,构造指定大小的队列。默认不限定大小

        • (2)put(item, block=True, timeout=None):向队列中添加一个项。默认阻塞,即队列满的时候,程序阻塞等待

        • (3)get(block=True, timeout=None):从队列中拿出一个项。默认阻塞,即队列为空的时候,程序阻塞等待

      • 图示

    • 基于queue.Queue的生产者和消费者模型

  • 6 小结

    • 线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。

    • 通过创建threading.Thread对象实例,可以创建线程;调用Thread对象的start()方法,可启动线程。也可以创建Thread的派生类,重写run方法,然后创建其对象实例来创建线程。

    • Lock对象锁解决线程安全问题:acquire()获得锁;release() 释放锁

    • queue模块提供了线程之间的数据传递:put()添加元素;get()获取元素

实验

一、实验目的:

1. 掌握pycharm的使用

2. 掌握python的面向对象的程序设计概念

3. 掌握多线程的使用

4. 理解python多线程互斥锁

二、实验环境

本次实验需使用实验室提供的Windows主机环境+pycharm

三、实验内容

说明:基础题为必做题,提高题为选做题

1.(基础题)编写程序实现:创建T1、T2、T3三个线程,怎样保证线程的执行顺序为T2->T1->T3?

import time

import threading

class TestThread(threading.Thread):

    def __init__(self, thread_name):

        super(TestThread, self).__init__()

        self.thread_name = thread_name

    def run(self):

        time.sleep(2)

        print('Thread:%s\t开始执行' % self.thread_name)

if __name__ == "__main__":

# 编写代码

  1. import time
  2. import threading
  3. class TestThread(threading.Thread):
  4. def __init__(self, thread_name):
  5. super(TestThread, self).__init__()
  6. self.thread_name = thread_name
  7. def run(self):
  8. time.sleep(2)
  9. print('Thread:%s\t开始执行' % self.thread_name)
  10. if __name__ == "__main__":
  11. t1 = TestThread("T1")
  12. t2 = TestThread("T2")
  13. t3 = TestThread("T3")
  14. t2.start() # 首先启动T2
  15. t2.join() # 等待T2执行完成
  16. t1.start() # 然后启动T1
  17. t1.join() # 等待T1执行完成
  18. t3.start() # 最后启动T3
  19. t3.join() # 等待T3执行完成

2. (基础题)将下列程序进行修改,要求每次运行结果输出样式为:(注:采用lock)

import threading,time
def job1():
    global A
    for i in range(10):
      A += 1
      print('job1',A)
def job2():
    global A
    for i in range(10):
        A += 10
        print('job2',A)
if __name__ =='__main__':
    A = 0
    t1 = threading.Thread(target=job1)
    t2 = threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

  1. import threading
  2. import time
  3. def job1():
  4. global A
  5. lock.acquire()
  6. for i in range(10):
  7. A += 1
  8. print('job1', A)
  9. lock.release()
  10. def job2():
  11. global A
  12. lock.acquire()
  13. for i in range(10):
  14. A += 10
  15. print('job2', A)
  16. lock.release()
  17. if __name__ == '__main__':
  18. A = 0
  19. lock = threading.Lock() # 创建锁对象
  20. t1 = threading.Thread(target=job1)
  21. t2 = threading.Thread(target=job2)
  22. t1.start()
  23. t2.start()
  24. t1.join()
  25. t2.join()

3.(提高题)编写程序实现:使用多线程实现一个模拟银行取钱的程序,要求:

  1. 使用全局变量作为余额;
  2. 存取钱时都只能有一个线程访问全局变量
  3. 取钱线程和存钱线程同时运行,每次存取随机数量的金额,直到取钱余额不足时则打印出相关信息,并结束程序。
  1. import threading
  2. import time
  3. import random
  4. class BankAccount:
  5. def __init__(self, balance):
  6. self.balance = balance
  7. self.lock = threading.Lock()
  8. def deposit(self, amount):
  9. with self.lock:
  10. time.sleep(random.random()) # 模拟处理时间
  11. self.balance += amount
  12. print(f"存入{amount}元,当前余额:{self.balance}元")
  13. def withdraw(self, amount):
  14. with self.lock:
  15. time.sleep(random.random()) # 模拟处理时间
  16. if self.balance >= amount:
  17. self.balance -= amount
  18. print(f"取出{amount}元,当前余额:{self.balance}元")
  19. else:
  20. print(f"余额不足,无法取出{amount}元")
  21. return False
  22. return True
  23. def deposit_thread(account):
  24. while True:
  25. amount = random.randint(100, 500)
  26. account.deposit(amount)
  27. def withdraw_thread(account):
  28. while True:
  29. amount = random.randint(50, 300)
  30. if not account.withdraw(amount):
  31. break
  32. if __name__ == "__main__":
  33. account = BankAccount(1000)
  34. deposit_thread = threading.Thread(target=deposit_thread, args=(account,))
  35. withdraw_thread = threading.Thread(target=withdraw_thread, args=(account,))
  36. deposit_thread.start()
  37. withdraw_thread.start()
  38. deposit_thread.join()
  39. withdraw_thread.join()

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/866360
推荐阅读
相关标签
  

闽ICP备14008679号