面试题答案
一键面试多线程与多进程资源共享机制的不同
- 内存空间
- 多线程:
- 同一进程内的多个线程共享进程的内存空间,包括全局变量、堆内存等。这意味着线程之间可以方便地访问和修改共享数据,无需额外的通信机制来传递数据。例如:
- 多线程:
import threading
num = 0
def increment():
global num
num += 1
threads = []
for _ in range(10):
t = threading.Thread(target = increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(num)
- 然而,这种共享也带来了同步问题,如多个线程同时修改共享变量可能导致数据不一致,像上面代码如果多个线程同时执行`num += 1`操作,由于`num += 1`并非原子操作,实际得到的`num`值可能小于10。
- **多进程**:
- 每个进程都有独立的内存空间,进程之间的数据是隔离的,相互不直接共享内存。不同进程有自己独立的堆、栈和全局变量空间。例如:
import multiprocessing
def increment():
num = 0
num += 1
print(num)
processes = []
for _ in range(10):
p = multiprocessing.Process(target = increment)
processes.append(p)
p.start()
for p in processes:
p.join()
- 这里每个进程中的`num`变量都是独立的,一个进程对`num`的修改不会影响其他进程中的`num`值。
2. 文件描述符 - 多线程: - 线程共享进程打开的文件描述符。例如,如果一个线程打开了一个文件,其他线程也可以直接操作这个文件描述符进行读写等操作。
import threading
import os
def write_to_file():
with open('test.txt', 'w') as f:
f.write('This is written by a thread')
t = threading.Thread(target = write_to_file)
t.start()
t.join()
- **多进程**:
- 虽然进程可以继承父进程打开的文件描述符,但它们是独立的副本。每个进程对文件描述符的操作相互独立。例如:
import multiprocessing
import os
def write_to_file():
with open('test.txt', 'w') as f:
f.write('This is written by a process')
p = multiprocessing.Process(target = write_to_file)
p.start()
p.join()
- 如果多个进程同时写同一个文件,可能需要额外的同步机制(如文件锁)来避免数据混乱。
实际编程中利用或规避差异
- 利用多线程共享资源
- 场景:适用于I/O密集型任务,且需要频繁访问共享数据的场景,如网络爬虫中多个线程共享一个URL队列和数据存储结构。
- 示例:
import threading
import queue
url_queue = queue.Queue()
data_list = []
def crawler():
while True:
url = url_queue.get()
# 模拟爬取数据
data = f"Data from {url}"
data_list.append(data)
url_queue.task_done()
urls = ["url1", "url2", "url3"]
for url in urls:
url_queue.put(url)
for _ in range(3):
t = threading.Thread(target = crawler)
t.daemon = True
t.start()
url_queue.join()
print(data_list)
- 规避多线程共享资源的问题
- 方法:使用锁(
threading.Lock
)、信号量(threading.Semaphore
)等同步机制。例如,修改前面num
变量的例子:
- 方法:使用锁(
import threading
num = 0
lock = threading.Lock()
def increment():
global num
with lock:
num += 1
threads = []
for _ in range(10):
t = threading.Thread(target = increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(num)
- 利用多进程的独立资源
- 场景:适用于CPU密集型任务,利用多个进程充分利用多核CPU资源。例如并行计算任务,每个进程独立进行计算,互不干扰。
- 示例:
import multiprocessing
def square(x):
return x * x
if __name__ == '__main__':
numbers = [1, 2, 3, 4, 5]
with multiprocessing.Pool(processes = multiprocessing.cpu_count()) as pool:
results = pool.map(square, numbers)
print(results)
- 在多进程间共享数据
- 方法:使用
multiprocessing.Value
和multiprocessing.Array
来共享简单数据类型和数组,或者使用Manager
类来创建更复杂的共享对象。例如:
- 方法:使用
import multiprocessing
def increment_shared_value(shared_num):
with shared_num.get_lock():
shared_num.value += 1
if __name__ == '__main__':
shared_num = multiprocessing.Value('i', 0)
processes = []
for _ in range(10):
p = multiprocessing.Process(target = increment_shared_value, args=(shared_num,))
processes.append(p)
p.start()
for p in processes:
p.join()
print(shared_num.value)