MST

星途 面试题库

面试题:Python中多线程与多进程在资源共享方面的差异

请详细阐述Python中多线程与多进程在资源共享机制上有哪些不同?并举例说明在实际编程中如何利用或规避这些差异。
47.0万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

多线程与多进程资源共享机制的不同

  1. 内存空间
    • 多线程
      • 同一进程内的多个线程共享进程的内存空间,包括全局变量、堆内存等。这意味着线程之间可以方便地访问和修改共享数据,无需额外的通信机制来传递数据。例如:
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()
  - 如果多个进程同时写同一个文件,可能需要额外的同步机制(如文件锁)来避免数据混乱。

实际编程中利用或规避差异

  1. 利用多线程共享资源
    • 场景:适用于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)
  1. 规避多线程共享资源的问题
    • 方法:使用锁(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)
  1. 利用多进程的独立资源
    • 场景:适用于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)
  1. 在多进程间共享数据
    • 方法:使用multiprocessing.Valuemultiprocessing.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)