知识点
-  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__":
# 编写代码

import time
import threadingclass TestThread(threading.Thread):def __init__(self, thread_name):super(TestThread, self).__init__()self.thread_name = thread_namedef run(self):time.sleep(2)print('Thread:%s\t开始执行' % self.thread_name)if __name__ == "__main__":t1 = TestThread("T1")t2 = TestThread("T2")t3 = TestThread("T3")t2.start()  # 首先启动T2t2.join()   # 等待T2执行完成t1.start()  # 然后启动T1t1.join()   # 等待T1执行完成t3.start()  # 最后启动T3t3.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()
import threading
import timedef job1():global Alock.acquire()for i in range(10):A += 1print('job1', A)lock.release()def job2():global Alock.acquire()for i in range(10):A += 10print('job2', A)lock.release()if __name__ == '__main__':A = 0lock = threading.Lock()  # 创建锁对象t1 = threading.Thread(target=job1)t2 = threading.Thread(target=job2)t1.start()t2.start()t1.join()t2.join()3.(提高题)编写程序实现:使用多线程实现一个模拟银行取钱的程序,要求:
- 使用全局变量作为余额;
- 存取钱时都只能有一个线程访问全局变量
- 取钱线程和存钱线程同时运行,每次存取随机数量的金额,直到取钱余额不足时则打印出相关信息,并结束程序。
 
import threading
import time
import randomclass BankAccount:def __init__(self, balance):self.balance = balanceself.lock = threading.Lock()def deposit(self, amount):with self.lock:time.sleep(random.random())  # 模拟处理时间self.balance += amountprint(f"存入{amount}元,当前余额:{self.balance}元")def withdraw(self, amount):with self.lock:time.sleep(random.random())  # 模拟处理时间if self.balance >= amount:self.balance -= amountprint(f"取出{amount}元,当前余额:{self.balance}元")else:print(f"余额不足,无法取出{amount}元")return Falsereturn Truedef deposit_thread(account):while True:amount = random.randint(100, 500)account.deposit(amount)def withdraw_thread(account):while True:amount = random.randint(50, 300)if not account.withdraw(amount):breakif __name__ == "__main__":account = BankAccount(1000)deposit_thread = threading.Thread(target=deposit_thread, args=(account,))withdraw_thread = threading.Thread(target=withdraw_thread, args=(account,))deposit_thread.start()withdraw_thread.start()deposit_thread.join()withdraw_thread.join()





































