- 练习进程和线程的创建以及使用方法
实验步骤
多任务
计算机早已进入多CPU或多核时代,而我们使用的操作系统都是支持“多任务”的操作系统
- 我们可以同时运行多个程序
- 可以将一个程序分解为若干个相对独立的子任务
- 让多个子任务并发的执行,从而缩短程序的执行时间
- 同时也让用户获得更好的体验。
- 实现让程序同时执行多个任务也就是常说的“并发编程”
Python既支持多进程又支持多线程,因此使用Python实现并发编程主要有3种方式:
多进程、多线程、多进程+多线程。
1.并行与并发的区别
- 并行:同时做某些事,可以互相不干扰的同一个时刻做几件事。(例如:高速公路,双向八车道,所有车都可以互不干扰的运行。)
- 并发:同一时刻,有很多事情要做。(例如:乡村小路,只有一个车道,一个方向同一时间只能过一辆车。发生多辆车同时同行的情况就是并发。)
- 可以用并行、队列、缓存区解决并发问题。
- 排队是解决并发的办法之一。
2.并发的解决
”食堂打饭模型“
中午12点,开饭了,所有学生都涌向了食堂。这就是并发,如果人特别多,就是高并发。
1.队列、缓冲区
假设只有一个窗口,就只能排队打菜。
排队就是把人排成队列,解决了资源使用的问题。
排成的队列,其实就是一个缓冲地带,就是缓冲区
假设女士优先,窗口就要有两队,只要有女生就优先打饭,男生就等着,女生队伍就是一个优先队列。
2.争抢
只开一个窗口,没有秩序,谁能挤进去就给谁打饭。窗口不能给其他人打饭。这是一种锁机制.
争抢也是一种解决高并发的方式,但是不好,因为有可能会有人很长时间抢不到。
3.预处理
如果打饭慢的原因是因为要现做的话,可以提前统计大多数人喜欢吃的菜品,提前做好,保证供应,提高打饭效率。
预处理的思想是加快用户获取数据的速度,常用缓存解决。
4.并行
由于吃饭的人数过多,一个窗口忙不过来,食堂老板决定扩大食堂,多开几个窗口,但是没有窗口都要雇人,造成成本上升。
企业可以通过购买更多服务器,或开启更多进程的方式做并行处理,来解决问题。(水平扩展)
注意:线程如果只在单个CPU核心上运行就并不是并行。
5.提速
提高单个窗口的打饭速度,也是解决并发的方式。
提高打饭人员技能,或为单个窗口配置更多服务人员。
提高单机的性能,或者单个服务器安装更多CPU.
这是一种垂直提升的思路
6.多地就近原则
多开几个食堂,分流。可以把食堂设置在宿舍附近,来提升吃饭速度。
二.多线程
线程是CPU分配资源的基本单位。当一程序开始运行,这个程序就变成了一个进程,而一个进程相当于一个或者多个线程。当没有多线程编程时,一个进程相当于一个主线程;当有多线程编程时,一个进程包含多个线程(含主线程)。使用线程可以实现程序大的开发。
优点:
- 1.多个线程可以在同一个程序中运行,并且每一个线程完成不同的任务。
- 2.多线程实现后台服务程序可以同时处理多个任务,并不发生阻塞现象。
- 3.Python3 通过两个标准库 thread 和 threading 提供对线程的支持,由于thread只是为了兼容python2的thread模块,所以推荐使用threading模块实现多线程。
threading提供了如下方法:
-
run(): 用以表示线程活动的方法。
-
start():启动线程活动。
-
join([time]): 等待至线程中止。
三.创建线程
创建线程有两种方式
语法:
thread.Thread(group=Nore,targt=None,args=(),kwargs={},*,daemon=None)
- 1.~group:必须为None,于ThreadGroup类相关,一般不使用。
- 2.~target:线程调用的对象,就是目标函数。
- 3.~name:为线程起这个名字。
- 4.~args:为目标函数传递关键字参数,字典。
- 5.~daemon(守护进程):用来设置线程是否随主线程退出而退出。
三.函数创建线程
import time
import threading
def test (x,y):
for i in range(x,y):
time.sleep(1)
print(i)
thread1 = threading.Thread(name='t1',target= test,args=(1,10))
thread2 = threading.Thread(name='t2',target= test,args=(11,20))
thread1.start() #启动线程1
thread2.start() #启动线程2
1.类创建线程
import time
import threading
class mythread(threading.Thread):
def run(self):
for i in range(1,10):
print(i)
time.sleep(1)
thread1 = mythread()
thread2 = mythread()
thread1.start()
thread2.start()
2.主线程
- 1.在python中,主线程是第一个启动的线程。线程包括主线程和子线程
- ~父线程:如果启动线程A中启动了一个线程B,A就是B的父线程。
- ~子线程:B就是A的子线程。
- 2.创建线程时有一个damon属性,用它来判断主线程。
- 当daemon设置False时,线程不会随主线程退出而退出,主线程会一直等着子线程执行完;。当daemon设置True时,线程会随主线程退出而退出,主线程结束其他的子线程会强制退出。
- 3.使用daemon注意:
~daemon属性必须在start( )之前设置,否则会引发RuntimeError异常
~每个线程都有daemon属性,可以显示设置也可以不设置,不设置则取默认值None
~如果子线程不设置daemon属性,就取当前线程的daemon来设置它。子子线程继承子线程的daemon值,作用和设置None一样。
~从主线程创建的所有线程不设置daemon属性,则默认都是daemon=False。
4.代码
import time
import threading
def tes1():
time.sleep(10)
for i in range(10):
print(i)
thread1 = threading.Thread(target=tes1,daemon=False)
thread1.start()
print('主线程完成了')
# 解释:当主线程运行完毕输出完之后,等待一下后输出0~9。如果将daemon=False改为daemon=True,则不会运行for i in range(10)语句。
四、阻塞线程
一个线程中调用另一个线程的join方法,调用者被阻塞,直到调用线程被终止。
1.语法形式:
join(timeout-=None)
2.timeout 参数指定调用者等待多久,没有设置时,就一直等待被调用线程结束被调用线程结束。
其中,一个线程可以被join多次调用。
import time
import threading
def tes1():
time.sleep(5)
for i in range(10):
print(i)
thread1=threading.Thread(target=tes1)
thread1.start()
thread1.join()
print('主线程完成了')
在thread1.start()后加thread1.join()添加join方法,输出时,主线程就会等待输出完0~9后再执行自己的print输出
多进程
众所周知,python的多线程提供的只是并发性,不会实际加快运行效率。而多进程则是用资源换取效率,可以实现真正的并行性。对于程序员而言,掌握多进程非常有必要。
多线程分为单线程和多线程
# 单线程
有一碗汤和一碗饭,正常操作是一口饭一口汤(当然你先把汤或者饭一口气吃完,当我无话可说)。单进程只能是先吃完饭,再喝汤;或者是先喝完汤,再吃饭。
import time
def eat():
for i in range(3):
print("吃饭……")
time.sleep(1)
def drink():
for i in range(3):
print("喝汤……")
time.sleep(1)
eat()
drink()
#先吃完所有饭,再喝完了所有汤 ,显然这种结果不是我们想要的
一.实现多线程
1.导入包
import multiprocessing
2、通过进程类创建进程对象
进程对象 = multiprocessing.Process(target=*) 此处target的值可以是函数名称
3、启动进程执行任务
进程对象.start()
# 多线程实现吃饭喝汤交叉进行
import multiprocessing
import time
def drink():
for i in range(3):
print("喝汤……")
time.sleep(1)
def eat():
for i in range(3):
print("吃饭……")
time.sleep(1)
if __name__ == '__main__':
# target:指定函数名
drink_process = multiprocessing.Process(target=drink)
eat_process = multiprocessing.Process(target=eat)
drink_process.start()
eat_process.start()
带参多进程
用到的是args和kwargs,其中args是使用元素组的方式给指定任务传参,kwargs是使用字典方式给指定任务传参。
args使用方式
进程对象 = multiprocessing.Process(target=*,args=(*,))
kwargs使用方式
进程对象 = multiprocessing.Process(target=*,kwargs={"变量名": 变量值})
import time
import multiprocessing
def eat(num, name):
for i in range(num):
print(name + "吃一口……")
time.sleep(1)
def drink(num, name):
for i in range(num):
print(name + "喝一口……")
time.sleep(1)
if __name__ == '__main__':
# target:指定执行的函数名
# args:使用元组方式给指定任务传参
# kwargs:使用字典方式给指定任务传参
eat_process = multiprocessing.Process(target=eat, args=(3, "阿giao"))
drink_process = multiprocessing.Process(target=drink, kwargs={"num": 4, "name": "阿giao"})
五.进程的编号 id
获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。
获取进程编码的两种操作:
-
os.getpid() 获取当前进程编号。
-
os.getppid() 获取当前父进程编号。
import time
import multiprocessing
import os
def dance():
# 获取子进程id,因为当前函数就是子进程
print('dance子进程id:',os.getpid())
# 获取dance子进程的父进程
print('dance的父进程:',os.getppid())
for i in range(5):
time.sleep(1)
print("跳舞",i)
def sing():
# 获取子进程id
print('sing子进程id:',os.getpid())
# 获取sing子进程的父进程
print('sing的父进程:',os.getppid())
for i in range(5):
time.sleep(1)
print("唱歌",i)
if __name__ == '__main__':
# 获取当前主进程的id编号
print('主进程id:',os.getpid())
my_dance = multiprocessing.Process(target=dance)
my_sing = multiprocessing.Process(target=sing)
my_sing.start()
my_dance.start()
六.线程池和进程池
concurrent包
concurrent.futures
Python 3.x版本中引入的模块
from concurrent import futures
futures.ThreadPoolExecutor() # 异步调用线程池
futures.ProcessPoolExecutor() # 异步调用进程池
使用的时候首先要定义一个池的执行器对象,Executor类子类对象。
方法 | 作用 |
ThreadPoolExecutor(max_worker=1) | 池中至多创建max_workers个线程池来同时异步执行,返回Executor实例 |
submit(fn,*args,**kwargs) | 提交执行的函数及其参数,返回Future实例 |
shutdown(wait=True) | 清理池 |
i
mport time
from concurrent.futures import ThreadPoolExecutor
def tes1(max):
time.sleep(1)
print(max)
with ThreadPoolExecutor(max_workers=2)as pool:
result = pool.map(tes1,('hello','world','你好','世界'))
for r in result:
print(r)
import time
from multiprocessing import Pool
def tes1(max):
print(max)
time.sleep(3)
if __name__ =='__main__':
pool = Pool(processes=10)
for i in range(100):
pool.apply(tes1,args=(i,))
print('完成')
pool.close()
pool.join()