Python百战 网络与并发编程 1 并发编程
并发编程介绍
串行,并行,并发的区别
串行(serial):一个CPU上,按顺序完成多个任务
并行(parallelism):指的是任务数小于等于cpu核数,即任务真的是一起执行的
并发(concurrency):一个CPU采用时间片管理方式,交替的处理多个任务。一般是是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去像一起执行
进程、线程、协程的区别
一条生产线就是一个进程
一个工人就是一个线程
工人不忙时干点其他事就是协程
进程(Process):拥有自己独立的堆和栈,既不共享堆,也不共享栈,进程由操作系统度;
进程切换需要的资源很最大,效率低
线程(Thread):拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系调
度;线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程(coroutine):拥有自己独立的栈和共享的堆,共享堆,不共享栈,协程由程序员协程的代码里显示调度;协程切换任务资源很小,效率高
同步和异步介绍
同步(synchronous):A调用B,等待B返回结果后,A继续执行
异步(asynchronous ):A调用B,A继续执行,不等待B返回结果;B有结果了,通知A,A再做处理
线程
特点:
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进中的实际运作单位
线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系统调度;
调度和切换:线程上下文切换比进程上下文切换要快得多。
线程的创建方式
_thread 是低级模块, threading 是高级模块
线程的创建可以通过分为两种方式:
- 方法包装
- 类包装
线程的执行统一通过 start() 方法
`from threading import Thread
from time import sleep
def func1(name):
for i in range(3):
print(f"thread:{name} :{i}")
sleep(1)
if name == 'main':
print("主线程,start")
# 创建线程
t1 = Thread(target=func1, args=("t1",))
t2 = Thread(target=func1, args=("t2",))
# 启动线程
t1.start()
t2.start()
print("主线程,end")`
join()
之前的代码主线程不会等待子线程结束,join可以让主线程等待子线程结束再结束主线程
`from threading import Thread
from time import sleep
def func1(name,age):
for i in range(2):
print(f"thread :{name}:{age}")
sleep(1)
if name == "main":
print("主线程.start")
#创建线程
t1 = Thread(target=func1, args=("小黑",99))
t1.start()
t1.join()
t2 = Thread(target=func1, args=("小白",88))
t2.start()
t2.join()
print("主线程结束")`
守护线程
随主线程死亡而死亡,为其他线程提供便利服务
`import threading
import time
def background_task():
print("守护线程开始")
time.sleep(3)
print("守护线程结束") # 可能不会执行(如果主线程提前退出)
创建守护线程
t = threading.Thread(target=background_task, daemon=True)
t.start()
print("主线程结束") # 主线程结束后,守护线程自动终止`
线程——全局解释器锁GIL问题
互斥锁
`import threading
共享变量
counter = 0
创建互斥锁(线程同步机制)
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
# 获取锁(确保同一时间只有一个线程执行下面代码)
lock.acquire()
counter += 1
# 释放锁(允许其他线程获取锁)
lock.release()
创建两个线程
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
启动线程
thread1.start()
thread2.start()
等待线程结束
thread1.join()
thread2.join()
预期结果:200000(100000 * 2)
实际结果:现在能正确得到 200000(因为锁保证了原子性)
print(f"Final counter value: {counter}")`
可重入锁
`import threading
shared_list = []
创建可重入锁(同一个线程可以多次获取)
rlock = threading.RLock()
def add_numbers():
for i in range(1000):
# 使用 with 自动管理锁(推荐写法)
with rlock:
shared_list.append(i)
创建两个线程
thread1 = threading.Thread(target=add_numbers)
thread2 = threading.Thread(target=add_numbers)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
预期:2000 个元素(1000 * 2)
实际:现在能正确得到 2000
print(f"List length: {len(shared_list)}")`
浙公网安备 33010602011771号