当前位置:   article > 正文

多进程与多线程_多线程和多进程

多线程和多进程

概念

多进程

多进程是指在操作系统中同时运行多个独立的进程,每个进程都有自己独立的内存空间和系统资源。进程是计算机中执行程序的实体,它包含了程序的代码、数据和执行状态等信息。

在多进程编程中,可以将任务分配给不同的进程来并行执行,从而充分利用多核处理器的计算能力。每个进程都有自己的执行环境,它们之间相互独立,互不干扰。多进程之间可以通过进程间通信(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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在这个例子中,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])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

其他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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

重点注意项

在使用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")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在上面的代码中,首先定义了一个函数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的网页内容,从而实现了多线程下载。注意,在实际的多线程编程中,可能需要考虑线程安全、同步机制和异常处理等问题,以确保程序的正确性和稳定性。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号