程序设计中并发(Concurrency)和并行(Parallelism)的区别
并发(Concurrency)和并行(Parallelism)都是多线程或者多进程处理任务的方式,但它们的概念和工作原理有着明显的区别:
基本概念:并发是指在一段时间内宏观上有多个程序在同时运行,但实际上每个程序只是在CPU分配的时间片内运行。并行则是指在同一时刻,有多个程序同时在运行,这通常需要有多个处理器或者多核的处理器才能实现。
实现方式:并发通常是通过时间片轮转的方式实现的,即在一个时间段内,CPU分配给每个程序一个时间片,程序在这个时间片内执行。当这个时间片用完后,CPU会切换到另一个程序执行。并行则是通过多个处理器或者多核的处理器同时执行不同的程序实现的,每个处理器或者核心都可以独立执行一个程序。
执行效果:并发的程序在宏观上看起来是同时运行的,但实际上在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。并行的程序则是在同一时刻有多个程序同时在执行,可以实现真正意义上的同时执行。
资源消耗:并发可以通过单个处理器实现,不需要额外的硬件支持,但可能会因为上下文切换等原因导致一定的性能开销。并行则需要多个处理器或者多核的处理器才能实现,需要额外的硬件支持,但可以充分利用硬件资源提高程序的执行效率。
适用场景:并发适用于I/O密集型任务,如网络请求、磁盘读写等,因为这些任务大部分时间都在等待I/O操作完成,通过并发可以提高CPU的利用率。并行适用于计算密集型任务,如科学计算、图像处理等,因为这些任务需要大量的计算资源,通过并行可以充分利用多核处理器的性能优势。
总的来说,并发和并行都是提高程序执行效率的重要手段,但需要根据具体的任务类型和硬件环境选择合适的方式。
并行的实现方式有以下几种:
多线程并行:基于多线程的并发任务设计,把不同的任务分配给操作系统某个进程的多个线程去处理,这样,各个线程只负责处理已分配的独享权限的任务,从而实现在单台处理机上的任务并发。实际上对一个进程而言在CPU上仍然是串行处理的。
多核并行:充分利用多核CPU的的每一个核去构建并行程序,而非像多线程那样去共享一个CPU核的进程资源,这种并行处理是高效的,然而基于这种方案的并行设计很可能比较复杂,工程实施和维护的代价也比较高。
并行任务本身的并发:更高层面的并发设计,他脱离了线程和进程层面,他把某个具体的任务和具体的处理机提前建立一个对应的map关系,任务处理机仅仅负责处理和他建立对应关系的任务,而对单个处理机而已仅仅是一个串行的任务处理机,这样整个并发模型的构建具有很强的灵活性和稳定性,尤其适应企业分布的环境的任务处理。
以上是并行的几种实现方式,需要根据具体的任务类型和硬件环境选择合适的方式。
举例:
(1) Python中实现并行处理的主要方式是使用multiprocessing模块。这个模块允许创建多个进程,并提供了进程之间的通信和同步的机制。下面是一个使用multiprocessing模块实现并行的简单示例:
import multiprocessing
import time
def worker(num):
"""进程函数"""
print('Worker:', num)
time.sleep(2)
if __name__ == '__main__':
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for process in processes:
process.join()
在这个例子中,我们创建了5个进程,每个进程都执行worker函数,并传递给它一个唯一的参数。然后我们启动这些进程,并使用join方法等待它们完成。这个程序会输出5个进程的执行结果,每个进程输出的时间可能会有所不同,因为它们是并行执行的。
另外,在Python中,也可以通过多线程实现并行处理。但是需要注意的是,由于Python的全局解释器锁(GIL)的存在,多线程在单核CPU上的并发效果并不明显,甚至可能会出现性能下降的情况。因此,在需要并发处理的时候,多进程是一种更好的选择。
(2) Python中的multiprocessing模块可以创建多个进程,并提供进程之间的通信和同步机制。当需要同时执行多个任务时,可以使用multiprocessing.Pool类创建一个进程池,并将任务分配给进程池中的进程执行。下面是一个使用multiprocessing.Pool的简单示例:
import multiprocessing
def square(n):
"""计算n的平方"""
return n ** 2
if __name__ == '__main__':
with multiprocessing.Pool(processes=4) as pool:
# 将任务列表中的每个任务分配给进程池中的一个进程执行
results = pool.map(square, [1, 2, 3, 4, 5])
print(results)
在这个例子中,我们定义了一个square函数,用于计算一个数的平方。然后我们使用multiprocessing.Pool类创建一个进程池,并将进程池中的进程数量设置为4。接着,我们使用pool.map()方法将一个包含5个数的列表作为任务列表,并将每个任务分配给进程池中的一个进程执行。最后,我们打印出任务列表中的每个数的平方结果。
需要注意的是,在使用multiprocessing.Pool时,需要将任务列表中的每个任务都包装在一个可序列化的容器中,例如元组或列表。这是因为进程之间通信时需要将任务序列化成字节流进行传输。如果任务是不可序列化的,将会抛出TypeError异常。