面试题答案
一键面试线程与进程的主要区别
- 资源分配:
- 进程:是资源分配的基本单位,每个进程都有独立的地址空间、内存、数据栈等资源。不同进程间的资源相互隔离,进程间通信(IPC)相对复杂,如使用管道、消息队列、共享内存等方式。
- 线程:是CPU调度的基本单位,线程共享所属进程的资源,如地址空间、文件描述符等。线程间通信较为简单,可直接共享内存,但可能会引发数据竞争等问题,需要使用锁机制来保证数据一致性。
- 创建与销毁开销:
- 进程:创建和销毁的开销较大,因为需要分配和释放大量系统资源。
- 线程:创建和销毁的开销较小,因为无需分配新的地址空间等资源。
- 并发性:
- 进程:多个进程可以并发执行,利用多核CPU的优势实现真正的并行计算。但进程间切换开销较大。
- 线程:多个线程在一个进程内并发执行,在单核CPU上,线程通过时间片轮转实现并发;在多核CPU上,也可利用多核优势,但由于GIL(全局解释器锁)的存在,Python中的多线程在同一时刻只能有一个线程执行Python字节码,对于CPU密集型任务,无法充分利用多核优势。
适用场景
- 适合使用线程的场景:
- I/O密集型任务:例如网络爬虫项目,大量时间花费在等待网络响应上。在爬虫程序中,一个线程发起网络请求后,会进入等待状态,此时其他线程可以继续工作,提高程序整体效率。示例代码如下:
import threading
import requests
def download(url):
response = requests.get(url)
print(f"Downloaded {url}, length: {len(response.content)}")
urls = [
'http://example.com',
'http://example.org',
'http://example.net'
]
threads = []
for url in urls:
t = threading.Thread(target=download, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
- 适合使用进程的场景:
- CPU密集型任务:比如科学计算、图像处理等。以图像的复杂滤镜处理为例,需要大量的CPU计算资源,使用多进程可以充分利用多核CPU的优势,加速处理过程。示例代码如下:
import multiprocessing
def heavy_compute(num):
result = 0
for i in range(100000000):
result += i * num
return result
if __name__ == '__main__':
numbers = [1, 2, 3, 4]
pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
results = pool.map(heavy_compute, numbers)
pool.close()
pool.join()
print(results)
这里在if __name__ == '__main__':
语句块中启动进程,是因为在Windows系统以及某些Unix系统衍生版本中,多进程编程需要这种方式来避免一些问题。