Python的进程、线程和协程

在Python中,进程、线程和协程是处理并发和并行任务的三种主要方式。下面我将对它们进行简要的比较,并提供一些示例代码。

进程(Process)

进程是操作系统进行资源分配和调度的基本单位,它包含独立的内存空间、系统资源以及执行一个或多个线程。

示例:使用multiprocessing模块创建进程
import multiprocessing  
  
def worker_process(num):  
    """子进程执行的函数"""  
    print(f'Worker Process {num} is running')  
  
if __name__ == "__main__":  
    for i in range(5):  
        p = multiprocessing.Process(target=worker_process, args=(i,))  
        p.start()

线程(Thread)

线程是操作系统调度的最小单位,它包含在进程之中,共享进程的资源(如内存空间、文件句柄等),但每个线程有独立的执行栈和线程局部存储。

# 示例:1、使用threading模块创建线程
import threading  
  
def worker_thread(num):  
    """子线程执行的函数"""  
    print(f'Worker Thread {num} is running')  
  
if __name__ == "__main__":  
    for i in range(5):  
        t = threading.Thread(target=worker_thread, args=(i,))  
        t.start()
# thread.Thread(group=Nore,targt=None,args=(),kwargs={},*,daemon=None)
# 参数解释:
# group:必须为None,于ThreadGroup类相关,一般不使用。
# target:线程调用的对象,就是目标函数。
# name:为线程起这个名字。默认是Tread-x,x是序号,由1开始,第一个创建的线程名字就是Tread-1。
# args:为目标函数传递关键字参数,字典。
# daemon:用来设置线程是否随主线程退出而退出。当daemon设置False时,线程不会随主线程退出而退出,主线程会一直等着子线程执行完;。当daemon设置True时,线程会随主线程退出而退出,主线程结束其他的子线程会强制退出


# 示例2:通过继承threading.Thread类的继承
import threading
class mythread(threading.Thread):
  def run(self):
    for i in range(1,10):
      print(i)
thread1 = mythread();
thread2 = mythread();
thread1.start()
thread2.start()

线程间通信

1. 使用共享数据
虽然 GIL 限制了多线程的并行执行,但它不限制线程之间的数据共享。你可以使用全局变量、类的属性或其他数据结构来在线程间共享数据。但是,需要小心处理数据竞争和条件竞争的问题,通常需要使用同步机制来确保数据的一致性和完整性

import threading  # python的锁
class mythread(threading.Thread):
 def run(self):
  global x                   #声明一个全局变量
  lock.acquire()             #上锁
  x +=10
  print('%s:%d'%(self.name,x))
  lock.release()             #解锁
x = 0                        #设置全局变量初始值
lock = threading.RLock()     #创建可重入锁
list1 = []                   
for i in range(5):   
 list1.append(mythread())    #创建五个线程,放到同一列表中
for i in list1:
 i.start()                   #开启列表线程

2. 使用 threading 模块中的同步机制
Python 的 threading 模块提供了多种同步机制,如锁(Lock 和 RLock)、条件变量(Condition)、事件(Event)和信号量(Semaphore),这些都可以用于多线程间的通信和同步。

锁(Lock):确保同一时间只有一个线程可以访问某个资源。
条件变量(Condition):允许一个或多个线程等待某个条件的发生,并在条件满足时被通知。
事件(Event):用于在线程间发送信号,一个线程可以等待某个事件的发生,而另一个线程可以触发该事件。
信号量(Semaphore):用于控制同时访问某个资源的线程数量

3. 使用 queue.Queue
Python 的 queue 模块提供了一个线程安全的队列实现,即 queue.Queue。多个线程可以安全地向队列中添加项目,并从队列中移除项目,而无需担心数据竞争或条件竞争的问题。这使得 queue.Queue 成为多线程间通信的一种非常有效的方式

import threading  
import queue

def producer(q):
    for i in range(5):
        item = f'item_{i}'
        q.put(item)
        print(f'Produced {item}')


def consumer(q):
    while True:
        item = q.get()
        if item is None:  # 使用 None 作为结束信号  
            break
        print(f'Consumed {item}')
        q.task_done()  # 表示之前入队的一个任务已经完成  


q = queue.Queue()
t1 = threading.Thread(target=producer, args=(q,))  
t2 = threading.Thread(target=consumer, args=(q,))  

t1.start()
t2.start()
t1.join()  # 等待生产者线程完成

# 发送结束信号
q.put(None)
t2.join()  # 等待消费者线程处理完所有项目

协程(Coroutine)

协程是一种用户态的轻量级线程,它的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

在Python中,通常使用async/await语法和asyncio库来实现协程。

# 示例:使用asyncio创建协程
import asyncio  
  
async def worker_coroutine(num):  
    """协程执行的函数"""  
    print(f'Worker Coroutine {num} is running')  
    await asyncio.sleep(0)  # 模拟I/O等待  
  
async def main():  
    tasks = [worker_coroutine(i) for i in range(5)]  
    await asyncio.gather(*tasks)  
  
if __name__ == "__main__":  
    asyncio.run(main())

比较

  1. 资源占用:进程拥有独立的内存空间,资源占用较大;线程共享进程资源,资源占用较小;协程几乎不占用系统资源,只消耗少量栈空间。
  2. 通信方式:进程间通信(IPC)通常通过管道、消息队列、共享内存等方式;线程间通信可以通过共享内存直接读写;协程间通信通常通过await表达式隐式进行。
  3. 切换开销:进程切换需要操作系统内核参与,开销较大;线程切换也需要操作系统内核参与,但开销相对较小;协程切换由用户态程序自行调度,开销极小。
  4. 并发能力:在Python中,由于全局解释器锁(GIL)的存在,多线程并不能实现真正的并行执行(在CPU密集型任务上);协程则可以在单线程中通过异步I/O实现高并发。
    总的来说,进程适用于需要独立资源的场景,线程适用于需要共享资源但不需要并行执行的场景,而协程则适用于需要高并发且主要是I/O密集型任务的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lance_mu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值