池的概念
由于服务器的硬件资源“充裕”,那么
提高服务器性能的一个很直接的方法就是以空间换时间
,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。
池是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配
。
当服务器进入正式运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配。很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,
因为分配系统资源的系统调用都是很耗时的
。当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用来释放资源
。
从最终效果来看,池相当于服务器管理系统资源的应用设施,它避免了服务器对内核的频繁访问。
池可以分为多种,常见的有
内存池
、进程池
、线程池
和连接池
。
执行多个任务怎么办?
一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。
还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。
同时
执行多个任务通常各个任务之间并不是没有关联
的,而是需要相互通信和协调
,有时,任务1必须暂停等待任务2完成后才能继续执行,有时,任务3和任务4又不能同时执行,多进程和多线程的程序的复杂度要远远高于单进程单线程的程序。
线程是最小的执行单元,而进程由至少一个线程组成。
如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
多线程
一个进程内可以有多个线程(至少一个主线程),多个线程共享该进程的所有变量
,同时对全局变量进行访问和改写很容易出现混乱
,所以需要用锁进行线程的同步控制(信号量,互斥锁
)
import time, threading
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n+1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s is end.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s is end.' % threading.current_thread().name)
多线程和多进程的实现很类似,进程启动时默认的线程为主线程,主线程之外可以开启新的子线程,current_thread()函数返回当前线程的实例
多进程
Linux/Unix操作系统提供了fork()系统调用,
fork执行一次会返回两次
,分别在父进程和子进程内返回(先父进程再子进程
),父进程返回的是子进程的ID
,子进程返回0
。父进程要记下所有子进程的ID,而子进程可以通过getppid()获得父进的ID
。
from multiprocessing import Process
import os
def run_proc(name):
print 'Run child process %s (%s)' % (name, os.getpid())
if __name__ == '__main__':
print 'Parent process %s.' % os.getpid()
p1 = Process(target=run_proc, args=('test1',))
p2 = Process(target=run_proc, args=('test2',))
p3 = Process(target=run_proc, args=('test3',))
p4 = Process(target=run_proc, args=('test4',))
print 'Process will start.'
p1.start()
p2.start()
p3.start()
p4.start()
p1.join()
p2.join()
p3.join()
p4.join()
进程池和线程池
关键性作用:
用户如果想创建新的进程,可以直接取得资源,从而避免了动态分配资源(这是很耗时的)
预先创建一组子进程,当有新任务来时,系统通过 调配 该组进程中的某个 子进程 完成此任务。
进程池
是由服务器预先创建的一组子进程,这些子进程的数目在 3~10 个之间(当然这只是典型情况)。线程池中的线程数量应该和 CPU 数量差不多。
所有子进程都运行着相同的代码,并具有相同的属性,比如优先级、 PGID 等
当有新的任务来到时,主进程将通过某种方式选择进程池中的某一个子进程来为之服务
最后用代码实现一下进程池:
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print 'Run task %s (%s)...' % (name, os.getpid())
start = time.time()
time.sleep(random.random()*3)
end = time.time()
print 'Task %s runs %0.2f seconds.' % (name, end-start)
if __name__ == '__main__':
print 'Parent process %s.' % os.getpid()
p = Pool()
for i in range(5):
p.apply_async(long_time_task, (i,))
print 'Waiting for all subprocesses done...'
p.close()
p.join()
print 'All subprocesses done.'
线程池
主要用于:
1)需要大量的线程
来完成任务,且完成任务的时间比较短
。
比如WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大。但对于长时间的任务,
比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2)对性能要求苛刻的应用
,比如要求服务器迅速响应客户请求。
3)接受突发性的大量请求
,但不至于使服务器因此产生大量线程的应用
并发
:实质 是 一个核物理cpu (或多核)在若干程序之间 多路复用,并发性是多事件对有限物理资源 强行共享提高效率.
互斥
:既一个公共资源同时只能被一个进程或一个线程使用。
并行
:指两个及以上的事件在同一时刻发生。可以使 多个程序 同时 在不同的cpu 上同时执行。多个实体,多个事件,同时发生。
异步
:是指进程 不需要 一直等 而是继续执行 下面的操作,不管其他进程的状态,当有消息返回时,系统会通知进程进行处理。
阻塞
:资源被占用 死等。厕所 被占 ,等着 里面的人出来。当调用某个方法,当前的执行流程 会被挂起;只有当这个函数返回结果,才会被继续执行。
非阻塞
:资源被占用 不等待 做其他事情。厕所 被占了 ,去吃饭。调用某个方法时,这个方法没有立刻返回 ,当前的 流程 不会被 挂起,继续执行自己的指令。
同步
:它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。
顺序执行 就是 就是并发,你一个人干可以多件事,时间是顺序执行
同时 执行两个步骤 ,那就是 并行处理
,那么这两个事件 ,也是异步
网友评论