怎样可以开网站,海南在线人才网招聘信息,wordpress 二级域名附件,刘淼 网站开发1. 死锁#xff08;Deadlock#xff09;
死锁指的是在多线程或者多进程的运行环境中#xff0c;两个或多个线程#xff08;进程#xff09;彼此等待对方释放所占用的资源#xff0c;进而陷入无限期等待的僵局#xff0c;最终导致程序无法继续推进。
必要条件
互斥条件…1. 死锁Deadlock
死锁指的是在多线程或者多进程的运行环境中两个或多个线程进程彼此等待对方释放所占用的资源进而陷入无限期等待的僵局最终导致程序无法继续推进。
必要条件
互斥条件资源在某一时间段内只能被一个进程线程独占使用。请求和保持条件进程线程已经持有了至少一个资源同时又发起了对其他已被别的进程线程占用资源的请求此时该进程线程会被阻塞但它不会释放自己已持有的资源。不剥夺条件进程线程已经获取的资源在使用完毕之前不会被强制剥夺只能由进程线程自身主动释放。循环等待条件在发生死锁时必然存在一个进程 - 资源的环形链即进程集合 {P0, P1, P2, …, Pn} 中P0 等待 P1 占用的资源P1 等待 P2 占用的资源依此类推Pn 等待 P0 占用的资源。
要点 解决破坏任一条件如统一资源申请顺序、超时释放。 实际开发中可通过 threading.Lock.acquire(timeout5) 设置超时避免死锁。
python
import threadinglockA threading.Lock()
lockB threading.Lock()def thread1():with lockA:print(Thread1 acquired A)with lockB: # 等待 B 释放可能死锁print(Thread1 acquired B)def thread2():with lockB:print(Thread2 acquired B)with lockA: # 等待 A 释放可能死锁print(Thread2 acquired A)t1 threading.Thread(targetthread1)
t2 threading.Thread(targetthread2)
t1.start(); t2.start()
t1.join(); t2.join() # 可能卡住
示例
在下面代码中如果线程 1 先获取了 lock1线程 2 先获取了 lock2接着线程 1 尝试获取 lock2线程 2 尝试获取 lock1就会发生死锁。
python
import threading# 创建两个锁
lock1 threading.Lock()
lock2 threading.Lock()def thread1_function():lock1.acquire()print(Thread 1 acquired lock 1)# 模拟一些工作try:# 尝试获取 lock2lock2.acquire()print(Thread 1 acquired lock 2)lock2.release()finally:lock1.release()def thread2_function():lock2.acquire()print(Thread 2 acquired lock 2)# 模拟一些工作try:# 尝试获取 lock1lock1.acquire()print(Thread 2 acquired lock 1)lock1.release()finally:lock2.release()# 创建并启动线程
t1 threading.Thread(targetthread1_function)
t2 threading.Thread(targetthread2_function)t1.start()
t2.start()t1.join()
t2.join()2. 多线程竞争访问控制
在多线程环境下为了避免对已访问的数据进行重复访问可以借助锁机制和标记位来实现对数据访问的控制。
要点 目标确保某数据仅被一个线程访问一次。 实现互斥锁、原子操作或标志位。
python
import threadingdata_accessed False
lock threading.Lock()def access_data():global data_accessedif lock.acquire(blockingFalse): # 非阻塞尝试获取锁if not data_accessed:print(f{threading.current_thread().name} 访问数据)data_accessed Truelock.release()threads [threading.Thread(targetaccess_data) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
示例
在下面代码中使用 threading.Event 更简洁
python
event threading.Event()
if not event.is_set() and event.set():print(访问数据)
示例
在下面代码中使用 visited 列表来标记每个数据是否已被访问使用 lock 来保证线程安全。每个线程在访问数据前会先检查标记位若数据未被访问则进行访问并更新标记位。
python
import threading# 共享数据
shared_data [1, 2, 3, 4, 5]
# 标记列表用于记录每个数据是否已被访问
visited [False] * len(shared_data)
# 锁用于线程同步
lock threading.Lock()def access_data():global shared_data, visitedwhile True:with lock:index Nonefor i in range(len(shared_data)):if not visited[i]:index ivisited[i] Truebreakif index is None:breakprint(fThread {threading.current_thread().name} accessed {shared_data[index]})threads []
for i in range(3):t threading.Thread(targetaccess_data)threads.append(t)t.start()for t in threads:t.join()3. 线程安全与互斥锁
要点 线程安全当多个线程访问某个类时无论运行时环境采用何种调度方式也不管这些线程如何交替执行并且在主调代码中无需额外的同步或协同操作这个类都能表现出正确的行为。简单来讲就是在多线程环境下对共享资源的访问不会引发数据不一致或其他异常问题。 互斥锁用于线程同步的机制它提供了排他性的访问控制。当一个线程获取了互斥锁后其他线程就无法再获取该锁必须等待持有锁的线程释放锁后才能继续竞争获取。 无锁线程安全使用 queue.Queue 或 collections.deque 等线程安全数据结构。
python
from threading import Lockcounter 0
lock Lock()def increment():global counterfor _ in range(1000):with lock: # 加锁保证原子性counter 1threads [threading.Thread(targetincrement) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
print(counter) # 正确输出 10000
示例
在下面代码中使用 threading.Lock() 创建了一个互斥锁 lock。在 increment 函数中通过 lock.acquire() 获取锁确保同一时间只有一个线程可以对 counter 进行修改修改完成后使用 lock.release() 释放锁。
python
import threading# 共享资源
counter 0
# 创建互斥锁
lock threading.Lock()def increment():global counterfor _ in range(100000):# 获取锁lock.acquire()try:counter 1finally:# 释放锁lock.release()threads []
for i in range(2):t threading.Thread(targetincrement)threads.append(t)t.start()for t in threads:t.join()print(counter)4. 同步、异步、阻塞、非阻塞
要点
同步在同步操作中调用者必须等待被调用的操作完成后才能继续执行后续代码程序的执行是按顺序依次进行的。异步异步操作中调用者发起操作后无需等待操作完成可继续执行后续代码。当异步操作完成后会通过回调函数、事件等方式通知调用者。阻塞执行某个操作时当前线程会被挂起直到该操作完成才能继续执行后续代码。例如调用阻塞的 I/O 操作时线程会一直等待直至操作完成。非阻塞执行操作时无论操作是否完成线程都不会被挂起会立即返回。线程可继续执行其他任务然后通过轮询等方式检查操作是否完成。
python
# 同步阻塞requests 库
import requests
response requests.get(https://example.com) # 阻塞直到响应返回# 异步非阻塞asyncio aiohttp
import aiohttp, asyncioasync def fetch():async with aiohttp.ClientSession() as session:async with session.get(https://example.com) as response:return await response.text()async def main():task asyncio.create_task(fetch())print(其他操作...) # 不阻塞content await task # 等待结果asyncio.run(main())
示例
在下面代码中sync_blocking 函数是同步阻塞的调用时主线程会等待 2 秒。async_non_blocking 函数是异步非阻塞的它会启动一个新线程来执行任务主线程不会等待该任务完成而是继续执行后续代码。
python
import time
import threading# 同步阻塞函数
def sync_blocking():print(Sync blocking operation started)time.sleep(2)print(Sync blocking operation finished)# 异步非阻塞函数
def async_non_blocking(callback):def task():print(Async non - blocking operation started)time.sleep(2)print(Async non - blocking operation finished)callback()t threading.Thread(targettask)t.start()# 回调函数
def callback_function():print(Callback function called)# 同步阻塞调用
sync_blocking()# 异步非阻塞调用
async_non_blocking(callback_function)
print(Main thread continues to execute)5. 僵尸进程与孤儿进程
要点
僵尸进程当子进程结束运行调用 exit 或 _exit后其进程描述符不会立即被删除而是会保留在系统中直到父进程调用 wait 或 waitpid 函数来获取子进程的退出状态。在此期间子进程处于僵尸状态称为僵尸进程。大量僵尸进程会占用系统资源。孤儿进程若父进程提前结束运行而其子进程仍在运行这些子进程就会成为孤儿进程。孤儿进程会被 init 进程进程 ID 为 1收养由 init 进程负责处理它们的退出状态。调用 wait 或 waitpid父进程在子进程结束后调用 wait 或 waitpid 函数来获取子进程的退出状态从而清除子进程的僵尸状态。信号处理父进程可以通过信号处理函数捕获 SIGCHLD 信号在信号处理函数中调用 wait 或 waitpid 函数。
python
import os, timepid os.fork()
if pid 0: # 子进程print(Child process exiting)
else: # 父进程time.sleep(30) # 父进程不调用 os.wait()子进程成为僵尸print(Parent process exiting)
示例
在下面代码中父进程通过 signal.signal(signal.SIGCHLD, sigchld_handler) 注册了一个信号处理函数 sigchld_handler当子进程结束时会触发 SIGCHLD 信号在信号处理函数中调用 os.waitpid 来处理子进程的退出状态避免僵尸进程的产生。
python
import os
import signal
import timedef sigchld_handler(signum, frame):while True:try:pid, status os.waitpid(-1, os.WNOHANG)if pid 0:breakexcept OSError:breaksignal.signal(signal.SIGCHLD, sigchld_handler)pid os.fork()
if pid 0:print(fChild process {os.getpid()} is running)time.sleep(2)print(fChild process {os.getpid()} is exiting)
else:print(fParent process {os.getpid()} is running)time.sleep(5)print(fParent process {os.getpid()} is exiting)6. 进程与线程使用场景
要点 1. 进程的使用场景
CPU 密集型任务对于需要大量计算的任务如科学计算、图像处理等使用多进程可以充分利用多核 CPU 的优势提高程序的执行效率。因为进程之间相互独立不受 GIL全局解释器锁的限制。隔离性要求高的任务当任务之间需要高度隔离例如一个任务可能会崩溃或出现异常为了不影响其他任务的执行可以使用多进程。 2. 线程的使用场景
IO 密集型任务对于需要大量 IO 操作的任务如网络请求、文件读写等使用多线程可以在 IO 操作时释放 GIL让其他线程继续执行从而提高程序的并发性能。任务之间共享数据频繁线程之间共享同一进程的内存空间因此在需要频繁共享数据的场景下使用多线程比多进程更方便。
python
# 多线程适合 IO
import threadingdef io_task():time.sleep(1) # 模拟 IOthreads [threading.Thread(targetio_task) for _ in range(100)]
for t in threads: t.start()# 多进程适合 CPU
from multiprocessing import Processdef cpu_task():sum(range(10**7)) # 模拟计算processes [Process(targetcpu_task) for _ in range(4)]
for p in processes: p.start()
示例
在下面代码中cpu_intensive_task 是 CPU 密集型任务使用多进程来处理io_intensive_task 是 IO 密集型任务使用多线程来处理。
python
import multiprocessing
import threading
import time# CPU 密集型任务
def cpu_intensive_task():result 0for i in range(1000000):result ireturn result# IO 密集型任务
def io_intensive_task():time.sleep(1)return# 多进程处理 CPU 密集型任务
if __name__ __main__:processes []for _ in range(4):p multiprocessing.Process(targetcpu_intensive_task)processes.append(p)p.start()for p in processes:p.join()# 多线程处理 IO 密集型任务
threads []
for _ in range(4):t threading.Thread(targetio_intensive_task)threads.append(t)t.start()for t in threads:t.join()7. 线程与进程的并发/并行
要点 线程由于 GIL 的存在多线程在 CPU 密集型任务中只能实现并发不能实现并行。并发是指在同一时间段内多个任务交替执行并行是指在同一时刻多个任务同时执行。但在 IO 密集型任务中多线程可以通过在 IO 操作时释放 GIL让其他线程继续执行从而实现并发。 进程多进程可以实现并行。因为每个进程都有自己独立的内存空间和 CPU 资源多个进程可以在多核 CPU 上同时执行提高程序的执行效率。
python
# 多线程受 GIL 限制
def count(n):while n 0: n - 1# 多线程执行时间 ≈ 单线程
t1 threading.Thread(targetcount, args(10**8,))
t2 threading.Thread(targetcount, args(10**8,))
t1.start(); t2.start() # 总时间 ≈ 单线程的两倍# 多进程真正并行
p1 Process(targetcount, args(10**8,))
p2 Process(targetcount, args(10**8,))
p1.start(); p2.start() # 总时间 ≈ 单线程的一半 示例
在下面代码中通过对比多线程和多进程执行 CPU 密集型任务的时间可以看出多进程在 CPU 密集型任务中能更好地利用多核 CPU 实现并行而多线程由于 GIL 的限制主要是并发执行。
python
import threading
import multiprocessing
import time# CPU 密集型任务
def cpu_task():result 0for i in range(1000000):result i# 多线程执行 CPU 密集型任务
start_time_thread time.time()
threads []
for _ in range(4):t threading.Thread(targetcpu_task)threads.append(t)t.start()for t in threads:t.join()
end_time_thread time.time()
print(fTime taken by threads: {end_time_thread - start_time_thread} seconds)# 多进程执行 CPU 密集型任务
if __name__ __main__:start_time_process time.time()processes []for _ in range(4):p multiprocessing.Process(targetcpu_task)processes.append(p)p.start()for p in processes:p.join()end_time_process time.time()print(fTime taken by processes: {end_time_process - start_time_process} seconds)8. 并行Parallel与并发Concurrency
要点 并发并发是指在同一时间段内多个任务交替执行。并发并不要求多个任务同时执行而是通过任务的切换来实现多个任务的同时推进。例如在单 CPU 系统中通过时间片轮转的方式多个线程可以交替执行看起来就像是同时执行一样。 并行并行指的是在同一时刻多个任务同时执行。这需要多个处理器或多核处理器的支持每个处理器可以同时处理一个任务从而提高程序的执行效率。例如在多核 CPU 的计算机上多个进程可以同时在不同的 CPU 核心上执行。
对比 并发单车道交替通行线程切换。 并行多车道同时行驶多核 CPU。 示例
在下面代码中使用多线程实现并发使用多进程模拟并行。
python
import threading
import time# 任务函数
def task():print(Task started)time.sleep(1)print(Task finished)# 并发示例
threads []
for _ in range(3):t threading.Thread(targettask)threads.append(t)t.start()for t in threads:t.join()# 并行示例借助多进程模拟
import multiprocessing
if __name__ __main__:processes []for _ in range(3):p multiprocessing.Process(targettask)processes.append(p)p.start()for p in processes:p.join()9. IO 密集型 vs CPU 密集型
要点
IO 密集型 定义IO 密集型任务在程序执行过程中大部分时间都花费在 IO 操作上如网络请求、文件读写等。CPU 在这些任务中大部分时间处于空闲状态。特点任务的执行时间主要取决于 IO 设备的性能而非 CPU 的性能。优化方式可以使用多线程或异步 IO 来提高程序的并发性能因为在 IO 操作时可以释放 CPU 资源让其他任务继续执行。 CPU 密集型 定义CPU 密集型任务在程序执行过程中大部分时间都花费在 CPU 计算上如科学计算、图像处理等。特点任务的执行时间主要取决于 CPU 的性能而非 IO 设备的性能。优化方式可以使用多进程来充分利用多核 CPU 的优势因为进程之间相互独立不受 GIL 的限制。
优化策略
类型优化方案Python 工具IO 密集型异步非阻塞、多线程asyncio, aiohttpCPU 密集型多进程、分布式计算multiprocessing 示例
在下面代码中io_task 是 IO 密集型任务使用多线程 python
import time
import threading
import multiprocessing# IO 密集型任务
def io_task():print(IO task started)time.sleep(2)print(IO task finished)# CPU 密集型任务
def cpu_task():result 0for i in range(10000000):result iprint(CPU task finished)# 多线程处理 IO 密集型任务
threads []
for _ in range(3):t threading.Thread(targetio_task)threads.append(t)t.start()for t in threads:t.join()# 多进程处理 CPU 密集型任务
if __name__ __main__:processes []for _ in range(3):p multiprocessing.Process(targetcpu_task)processes.append(p)p.start()for p in processes:p.join()10. asyncio 原理
要点 事件循环事件循环是asyncio的核心它负责调度和执行协程。事件循环会不断地从任务队列中取出待执行的协程执行协程直到遇到await关键字然后将协程挂起继续执行其他协程。当await等待的异步操作完成后事件循环会将挂起的协程恢复执行。 协程asyncio使用协程作为异步操作的基本单位。协程是一种轻量级的线程它可以在代码中暂停和恢复执行。在 Python 中使用async def定义协程函数使用await关键字来暂停协程的执行直到等待的异步操作完成。 Future和TaskFuture是一个表示异步操作结果的对象它可以用来跟踪异步操作的状态。Task是Future的子类它封装了一个协程用于在事件循环中执行。可以通过asyncio.create_task()函数来创建一个Task对象。 异步 IO 操作asyncio提供了一系列的异步 IO 操作如异步网络请求、异步文件读写等。这些异步 IO 操作会在底层使用非阻塞 IO 和事件通知机制当 IO 操作完成后会通过回调函数通知事件循环。 示例
在下面代码中定义了一个协程函数task在main函数中创建了多个Task对象并使用asyncio.gather()函数来等待所有任务完成。最后使用asyncio.run()函数来启动事件循环。
python
import asyncioasync def task(n):print(fTask {n} start)await asyncio.sleep(1) # 非阻塞等待print(fTask {n} end)async def main():await asyncio.gather(task(1), task(2)) # 并发执行asyncio.run(main())
# 输出
# Task 1 start → Task 2 start → 1秒后→ Task 1 end → Task 2 end 友情提示本文已经整理成文档可以到如下链接免积分下载阅读
https://download.csdn.net/download/ylfhpy/90406977