多进程与多线程
多进程是指在操作系统中同时运行多个独立的进程,每个进程都有自己独立的内存空间和系统资源。进程是计算机中执行程序的实体,它包含了程序的代码、数据和执行状态等信息。在多进程编程中,可以将任务分配给不同的进程来并行执行,从而充分利用多核处理器的计算能力。每个进程都有自己的执行环境,它们之间相互独立,互不干扰。多进程之间可以通过进程间通信(IPC)机制来进行数据交换和同步操作。多进程可以提高程序的执行效率
概念
多进程
多进程是指在操作系统中同时运行多个独立的进程,每个进程都有自己独立的内存空间和系统资源。进程是计算机中执行程序的实体,它包含了程序的代码、数据和执行状态等信息。
在多进程编程中,可以将任务分配给不同的进程来并行执行,从而充分利用多核处理器的计算能力。每个进程都有自己的执行环境,它们之间相互独立,互不干扰。多进程之间可以通过进程间通信(IPC)机制来进行数据交换和同步操作。
多进程可以提高程序的执行效率,特别适用于执行CPU密集型任务,如图像处理、数据计算、科学计算等。通过将任务分配给多个进程并行执行,可以加快任务的完成速度。
在Python中,可以使用multiprocessing模块来实现多进程编程。该模块提供了创建和管理进程的类和函数,以及进程间通信的工具,使得编写多进程程序变得简单和方便。
多线程
多线程是指在一个进程内同时执行多个线程,每个线程都是独立的执行序列,拥有自己的程序计数器、栈、寄存器和状态等信息。线程是操作系统调度的最小单位,它可以独立执行任务,并与其他线程共享进程的资源和内存空间。
与多进程相比,多线程具有以下特点:
1.轻量级:线程的创建和切换开销相对较小,可以更高效地利用系统资源。
2.共享内存:多个线程可以共享同一个进程的内存空间,可以直接访问共享数据,简化了数据交换和通信的过程。
3.响应性:多线程可以提高程序的响应性,因为一个线程的阻塞操作不会影响其他线程的执行。
4.并发性:多个线程可以同时执行,实现并发处理,提高程序的执行效率。
5.多线程适用于执行I/O密集型任务,如网络请求、文件读写、用户交互等。由于线程共享进程的资源,多线程编程需要注意线程安全性和共享数据的同步问题,避免出现竞态条件和数据不一致的情况。
在Python中,可以使用threading模块来实现多线程编程。该模块提供了创建和管理线程的类和函数,以及线程同步和互斥的工具,使得编写多线程程序变得简单和方便。
两者区别
多进程和多线程是实现并发的两种常见方式,它们有以下几个主要区别:
执行方式:多进程是在多个独立的进程中执行任务,每个进程有自己独立的内存空间和系统资源。而多线程是在同一个进程中的多个线程中执行任务,线程共享同一个进程的内存空间和系统资源。
资源占用:由于每个进程有自己独立的内存空间,多进程在一定程度上会占用更多的系统资源。而多线程共享同一个进程的资源,因此线程切换开销较小,占用的系统资源相对较少。
**切换开销:**切换进程的开销比切换线程的开销大。由于进程间的内存空间相互独立,切换进程需要保存和恢复更多的上下文信息。而线程切换只需要保存和恢复少量的上下文信息。
**编程模型:**多进程编程相对复杂,因为进程间通信需要额外的机制来实现,如队列、管道等。而多线程编程相对简单,线程之间可以通过共享内存来进行通信。
**扩展性:**多进程可以更好地利用多核处理器,因为不同进程可以在不同的核上并行执行。而多线程受限于全局解释器锁(GIL)的存在,在某些情况下无法充分利用多核处理器。
总的来说,多进程适合执行CPU密集型任务,可以充分利用多核处理器;而多线程适合执行I/O密集型任务,可以提高程序的响应性和并发性。选择使用多进程还是多线程,取决于具体的应用场景和需求。
使用案例
多进程
要在Python函数内实现多进程,可以使用multiprocessing模块的Pool类。Pool类提供了一种简单的方式来创建和管理进程池。下面是一个示例代码,演示了如何在函数内部使用进程池创建多个进程:
import multiprocessing
def worker(num):
"""子进程的工作函数"""
print(f'Worker {num} started')
# 执行一些任务
print(f'Worker {num} finished')
def main():
num_processes = 4
# 创建进程池
pool = multiprocessing.Pool(processes=num_processes)
# 使用进程池创建多个进程
for i in range(num_processes):
pool.apply_async(worker, args=(i,))
# 关闭进程池,不再接受新的任务
pool.close()
# 等待所有进程完成
pool.join()
if __name__ == '__main__':
main()
在这个例子中,worker函数是每个子进程要执行的工作函数。在main函数中,首先创建了一个进程池,指定了要创建的进程数量。然后,使用apply_async方法向进程池提交任务,每个任务都会在一个单独的进程中执行。最后,调用close方法关闭进程池,并使用join方法等待所有进程完成。
注意,在使用多进程时,需要将代码放在if name == ‘main’:条件下,以确保在主进程中执行。这是为了避免在Windows系统中创建多个进程时出现问题。
使用进程池可以更方便地管理多个进程,而不需要手动创建和启动每个进程。进程池会自动分配任务给可用的进程,并处理进程间通信和资源管理。
其他写法一:
class SelfMultiple:
def __init__(self, func, process: int, params: list, custom_callback=False, callback=None):
print("==>init customized multiple class")
self.func = func
self.params = params
self.process = process
self.custom_callback = custom_callback
self.callback = callback
def run(self):
self.pool = Pool(processes=self.process)
results = []
if self.custom_callback == False:
print("==>undefined self callback")
pbar = tqdm(total=len(self.params))
def update(*a):
pbar.update()
for param in self.params:
result = self.pool.apply_async(self.func, param, callback=update)
results.append(result)
else:
print("==>defined self callback")
print(f"==>executing || {self.func}")
for param in self.params:
result = self.pool.apply_async(self.func, param, callback=self.callback)
results.append(result)
self.pool.close()
self.pool.join()
return results
if __name__=="__main__":
multiple_tool = SelfMultiple(func, process=8, params=pr, custom_callback=False)
res = multiple_tool.run()
for k in res:
s_tdata = k.get()
results.append(s_tdata[1])
其他2:
# 多进程 main函数内
pool = multiprocessing.Pool(processes=2)
results = []
for n in kk:
result = pool.apply_async(calc_fea, (n,))
results.append(result)
pool.close()
pool.join()
res = [i.get() for i in results]
results = np.array(res)
重点注意项
在使用multiprocessing模块创建多进程时,将代码放在if name == ‘main’:条件下是一种良好的实践,特别是在Windows系统中。这是由于Windows系统的进程创建机制与其他操作系统有所不同。
在Windows系统中,当一个脚本文件被执行时,会创建一个主进程,并且该脚本文件中的全局变量和函数都会被加载。如果在主进程中创建新的子进程,那么这些子进程也会重新加载整个脚本文件,这可能会导致意外的行为或错误。
为了避免这种情况,Python提供了if name == ‘main’:条件,只有当脚本文件作为主程序执行时,才会运行该条件下的代码。在这种情况下,主进程会加载脚本文件,并执行main函数或其他入口点函数,而子进程则不会再次加载整个脚本文件。
因此,将多进程的相关代码放在if name == ‘main’:条件下是一种保险的做法,可以避免在Windows系统中出现意外的问题。对于其他操作系统,虽然不是必须的,但仍然建议遵循这种做法,以保持代码的一致性和可移植性。
多线程
在Python中,可以使用threading模块来实现多线程编程。下面是一个简单的示例代码,展示了如何创建和启动多个线程:
import threading
# 定义一个线程执行的函数
def thread_function(name):
print("Thread", name, "started")
# 线程要执行的任务
for i in range(5):
print("Thread", name, "working")
print("Thread", name, "finished")
# 创建线程对象
thread1 = threading.Thread(target=thread_function, args=(1,))
thread2 = threading.Thread(target=thread_function, args=(2,))
# 启动线程
thread1.start()
thread2.start()
# 等待线程结束
thread1.join()
thread2.join()
print("All threads finished")
在上面的代码中,首先定义了一个函数thread_function作为线程要执行的任务。然后通过threading.Thread类创建了两个线程对象thread1和thread2,分别指定了要执行的函数和传递给函数的参数。接着使用start()方法启动线程,线程开始执行函数中的任务。最后使用join()方法等待线程执行完毕。
注意,在多线程编程中,由于线程是并发执行的,可能会出现竞态条件(Race Condition)和数据不一致的问题。因此,在多个线程访问共享数据时,需要采取适当的同步机制(如锁、信号量、条件变量等)来保证数据的一致性和线程的安全性。
此外,Python还提供了concurrent.futures模块,它提供了更高级的接口来实现多线程和多进程编程,例如ThreadPoolExecutor和ProcessPoolExecutor,可以更方便地管理线程池和进程池。这些高级接口可以简化多线程编程的复杂性,推荐在实际开发中使用。
下面是一个使用多线程的简单案例,展示了如何同时下载多个网页的内容:
import threading
import requests
# 定义一个线程执行的函数
def download(url):
response = requests.get(url)
print("Downloaded", url)
# 创建要下载的网页列表
urls = [
"https://www.example.com",
"https://www.google.com",
"https://www.python.org"
]
# 创建线程对象列表
threads = []
for url in urls:
thread = threading.Thread(target=download, args=(url,))
threads.append(thread)
# 启动线程
for thread in threads:
thread.start()
# 等待线程结束
for thread in threads:
thread.join()
print("All downloads finished")
~~~
在上面的代码中,我们定义了一个函数download,它接收一个URL作为参数,并使用requests库下载该URL对应的网页内容。然后我们创建了一个包含多个URL的列表。接下来,我们创建了一个线程对象列表threads,并为每个URL创建一个线程对象,将download函数作为线程要执行的任务,并传递URL作为参数。然后我们使用start()方法启动每个线程,线程开始执行下载任务。最后使用join()方法等待所有线程执行完毕。
这样,每个线程将并发地下载对应URL的网页内容,从而实现了多线程下载。注意,在实际的多线程编程中,可能需要考虑线程安全、同步机制和异常处理等问题,以确保程序的正确性和稳定性。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)