线程
多任务:操作系统可以同时运行多个任务。
python 默认是单任务
线程: 被称为轻量级进程,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是CPU调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源
主线程:当⼀个程序启动时,就有⼀个进程被操作系统(OS)创建,与此同时⼀个线程也⽴刻运 ⾏,该线程通常叫做程序的主线程
主线程的重要性:
1)是产生其他子线程的线程
2)通常它必须最后完成执行各种关闭动作
子线程:可以看做是程序执⾏的⼀条分⽀,当⼦线程启动后会和主线程⼀起同时执⾏
使用threading .Thread(target=函数名,args=(参数列表,元组))创建子线程对象
import time
import threading
def sing():
for i in range(5):
print("正在唱歌 .....")
time.sleep(0.5)
def dance():
for i in range(5):
print("正在跳舞 .....")
time.sleep(0.5)
if __name__ == '__main__':
# 如果不使用线程,只能5次唱歌后才能进行5次跳舞,不能同时唱歌跳舞,而且速度慢
# sing()
# dance()
# 使用threading .Thread(target=函数名,args=(参数列表,元组))创建子线程对象
# 线程 传递参数有 三种方式
# 1.元组 threading.thread(target = 函数名,args=(参数1,参数2,......))元组中元素的顺序和函数的参数顺序一致
# 2.字典 threading.thread(target = 函数名,kwargs={“参数名”:"参数值",.....})
# 3.混合元组和字典 threading.thread(target = 函数名,args=(参数1,参数2,......),kwargs={“参数名”:"参数值",.....})
sing_thread = threading.Thread(target=sing)
dance_thread = threading.Thread(target=dance)
# 线程对象.start()启动子线程(子线程同时执行,这样就可以同时唱歌跳舞,但是多线程程序的执⾏顺序是不确定的)
sing_thread.start()
dance_thread.start()
# 使用多线程并发的操作,花费时间要短很多
除了使用threading.Thread创建线程,也可以通过自定义一个类继承threading.Thread类,并且重写run()方法,然后通过实例化自定义对象.start()方法启动自定义线程。调⽤start() ⽅法,但是对象的run⽅法被执⾏了,说明 start⽅法中调⽤了 run⽅法。其中的__init__方法需要调用父类的init方法,super().init()
查看线程数量:
threading.enumerate() 获取当前所有活跃线程的列表。使用len()对列表求长度可以看到当前活跃的线程的个数。
获取当前线程对象:
threading.current_thread() 获取当前线程对象(含名称)
守护线程:如果在程序中将⼦线程设置为守护线程,则该⼦线程会在主线程结束时⾃动退出,设 置⽅式为thread.setDaemon(True),要在thread.start()之前设置。默认是false的,也就是主线程结束 时,⼦线程依然在执⾏。
# 导入时间和线程模块
from time import sleep
import threading
def work1():
for i in range(10):
print("正在执行.....", i)
sleep(0.5)
if __name__ == '__main__':
# 创建子线程
t1 = threading.Thread(target=work1)
# ****设置子线程为守护线程,如果不设置这个,即使主线程exit,子线程也会继续执行****
t1.setDaemon(True)
# 启动子线程
t1.start()
sleep(2)
print("Game Over .....")
exit()
并发:
指的是任务数多于cpu核数,通过操作系统的各种任务调度算法,实现⽤多个任务“⼀起”执 ⾏(实际上总有⼀些任务不在执⾏,因为切换任务的速度相当快,看上去⼀起执⾏⽽已)
真正的并⾏执⾏多任务只能在多核CPU上实现,但是,由于任务数量远远多 于CPU的核⼼数量, 所以,操作系统也会⾃动把很多任务轮流调度到每个核 ⼼上执⾏。
并行:
指的是任务数⼩于等于cpu核数,即任务真的是⼀起执⾏的
多线程 共享 全局变量数据
产生的问题:
线程对全局变量随意修改可能造成多线程之间对全局变量的混乱(即线程⾮安全)
如果多个线程同时对同一个全局变量操作,会造成资源竞争的问题,导致计算结果有误。
解决方法:
优先让某个线程先执行,使用线程对象.join()方法解决,缺点:把多线程变成了单线程,影响了整体性能
如下:
import threading
from time import sleep
# 定义全局变量
num = 0
def work1():
global num
for i in range(1000000):
num += 1
print("work1 num = %d" % num) #1000000
def work2():
global num
for i in range(1000000):
num += 1
print("work2 num= %d" % num) #2000000
if __name__ == '__main__':
work1_thread = threading.Thread(target=work1)
work2_thread = threading.Thread(target=work2)
work1_thread.start()
# work1执行完后再执行work2
work1_thread.join()
work2_thread.start()
# while 循环的作⽤是:能保证⼦线程运⾏完毕,再去输出 num
while len(threading.enumerate()) != 1:
sleep(2)
print("num = ",num) # 2000000
同步:
在多任务中 多个任务执行有先后顺序,一个执行完毕后,另外一个再执行(这个可以解决线程同时修改全局变量)
异步:
在多任务中 多个任务执行没有先后顺序,多个任务同时执行
线程的锁机制:
当线程获取资源后,立刻进行锁定,资源使用完毕后再解锁,有效的保证同一时间只有一个线程在使用资源
互斥锁:
当多个线程几乎同时修改某一个共享数据的时候,需要进行共同控制。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
# 创建锁
mutex= threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
死锁:
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁
进程
进程: 是资源分配的最小单位,也是线程的容器。
程序:程序时固定不变的,而进程会根据运行需要,让操作系统动态分配各种资源(如内存,cpu,磁盘,网络)
进程的状态:
首先导包,import multiprocessing,使用multiprocessing.Process(target=函数名)创建进程对象,使用.start()方法启动进程,当然除了target这个参数,还可以用args元组方式和kwargs字典方式传参
获取当前进程对象名称:multiprocessing.current_process()
设置名称:multiprocessing.Process(target=xxx,name=“进程名称”)
获取进程号id:multiprocessing.current_process().pid / os.getpid()
获取进程父id:os.getppid()
进程之间 不能共享 全局变量
底层原理:子进程会复制主进程的资源到内部运行
给子进程传递参数方法和给子线程传递参数方法基本一致!
守护进程:p1.daemon=True 设置⼦进程 p1 守护主进程,当主进程结束的时候,⼦进程也随之结束。默认情况下,主进程会等待子进程结束才结束。
p1.terminate() 终⽌进程执⾏,并⾮是守护进程
进程间的通信:
消息队列
创建队列,队列的操作写入和读取,获取队列中消息的个数,判断队列是否为空或者已满==
```python
# 导入模块
import multiprocessing
# 定义消息队列,如果不指定队列长度,则默认为最大,
# 如果指定了消息队列的大小,则消息队列就有上限控制
queue = multiprocessing.Queue(4)
# queue.put()向消息队列中放入任意类型的值
queue.put(1)
queue.put("hello")
queue.put([1, 2, 3])
queue.put({
"name": "wenmei", "age": 18})
# 当超出队列长度时,进入阻塞状态,直到从消息队列腾出空间为止
#queue.put((1, 2, 3))
# 消息队列如果没有空间可写入,则会立刻抛出"Queue.Full"异常
queue.put_nowait((1, 2, 3))
# 判断队列是否已满
isFull = queue.full()
print("isFull -------->",isFull)
print(queue) # <multiprocessing.queues.Queue object at 0x0000021D09E957F0>
# queue.get 获取第一个值
value1 = queue.get()
print(value1)
# queue.get 获取第二个值
value2 = queue.get()
print(value2)
........
value5 = queue.get_nowait()
print(value5)
# 获取队列中消息的个数
print("消息的个数:",queue.qsize())
# 判断队列是否为空
isEmpty = queue.empty()
print("isEmpty------>", isEmpty)
"""
```python
思路:利用队列在两个进程间进行传递,进而实现数据共享
1.准备两个进程
2.准备一个队列,一个进程向队列写入数据,另一进程从队列读数据
"""
#导入模块
import multiprocessing
from time import sleep
# 写入数据到队列的函数
def wirte_queue(queue):
for i in range(10):
# 判断是否已满
if queue.full():
print("队列已满!")
break
queue.put(i)
print("写入成功,已经写入:" ,i)
sleep(0.5)
# 读取队列数据并显示的函数
def read_queue(queue):
while True:
if queue.empty():
print("队列为空!")
break
value = queue.get()
print("已经读取:", value)
if __name__ == '__main__':
# 创建一个空的队列
create_queue = multiprocessing.Queue(5)
# 创建2个进程,分别读写
wirte_process = multiprocessing.Process(target=wirte_queue,args=(create_queue,))
read_process = multiprocessing.Process(target=read_queue,args=(create_queue,))
wirte_process.start()
# 优先写数据,结束后再读取数据
wirte_process.join()
read_process.start()
进程池:当需要创建的⼦进程数量不多时,可以直接利⽤multiprocessing中的Process动态成⽣多个进程,但如果是上百甚⾄上千个⽬标,⼿动的去创建进程的⼯作量巨⼤,此时就可以⽤到multiprocessing模块提 供的Pool⽅法。
同步方式: pool.apply(),阻塞方式,也就是说,必须等待上一个进程退出才能执行下一个进程
异步方式: pool.apply_async(),非阻塞方式,并行,后面必须有pool.close() pool.join()
=====