惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
NISL@THU
NISL@THU
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Tenable Blog
Forbes - Security
Forbes - Security
V2EX - 技术
V2EX - 技术
Webroot Blog
Webroot Blog
Schneier on Security
Schneier on Security
T
The Exploit Database - CXSecurity.com
T
Tor Project blog
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
The Last Watchdog
The Last Watchdog
PCI Perspectives
PCI Perspectives
O
OpenAI News
C
Cyber Attacks, Cyber Crime and Cyber Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
量子位
D
Docker
AI
AI
Blog — PlanetScale
Blog — PlanetScale
S
Security @ Cisco Blogs
S
Schneier on Security
The GitHub Blog
The GitHub Blog
W
WeLiveSecurity
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog
C
Check Point Blog
A
About on SuperTechFans
D
Darknet – Hacking Tools, Hacker News & Cyber Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Engineering at Meta
Engineering at Meta
I
InfoQ
T
Threat Research - Cisco Blogs
Project Zero
Project Zero
Cloudbric
Cloudbric
MongoDB | Blog
MongoDB | Blog
Cisco Talos Blog
Cisco Talos Blog
L
Lohrmann on Cybersecurity
S
Securelist

博客园 - papering

为 with语句上下文提供的工具 懒加载 IDE发现 import Canvas 指纹 魔改chromium源码——CDP(Chrome DevTools Protocol)检测01 whether the browser environment is controlled by a robot. chromium指纹魔改 对拷线 rpa 任务编排 a JSON formatted stream to ``fp`` “幽灵字符”问题 浏览器背后的黑科技 多进程 多线程 callback technique: signals and slots chrome 浏览器 调试 数据采集 Chrome DevTools ProtocolCDP PyInstaller 实现延迟导入 Storage mount options 汽车 OTA 技术体系 使用 grep 的扩展正则表达式功能 Python 中双下划线会触发名称修饰(mangling),通常用于避免子类覆盖, 容器内没有安装curl 验证web服务存在 在容器内执命令 连接数据库 http响应的 headers中拿到文件名 复制python项目文件,包含 包依赖 物理机 虚拟机 云厂商 裸金属服务器 CPU 透传 CPU Passthrough Docker 环境权限与资源限制 can't start new thread Seccomp security profiles Don't let containers reboot the host. Registry Pattern search_after 深度分页 Scrolling is not intended for real time user requests no longer recommend using the scroll API for deep pagination point in time PIT preserve the current index state kafka 大消 小消 依赖于中央数据库来协调网络中的查询 使用泛滥式查询(query flooding)来查询文件,搜索网络中的所有节点 使用分布式散列表来查询网络中的文件 使用 DHT (Distributed Hash Table,分布式哈希表) 替代 Tracker,标志着 P2P 系统从“有中心”进化到了“完全去中心化”。 STUN 负责“问路”(发现 IP),Tracker 负责“找人”(发现节点),而打洞失败时则靠 TURN “代跑”(转发数据) p2p 视频传输方案 UDP 打洞 attached detached shared memory 共享内存 shm ipcs folders of instructions, scripts, and resources that Claude loads dynamically to improve performance on specialized tasks complete specific tasks in a repeatable way 词元 指令 分词器 向量化 计算关系 输出回答 QQ 25 年进化史:从UDP到NT架构,支撑亿级在线的技术之路 如何设计一个IM单聊架构 长链接业务层 短链接业务层 go java python 面向对象区别 常规价将被永久拉低,难以重建价格锚点 AI对比功能强调的是产品的真实价值 LLM provider configuration luckclaw AI 智能体 Gateway 网关 技术架构的每一次重大更新,都被称作一次“脱壳” 具备“手脚”去在真实操作系统中“执行”任务的数字实体 供应链攻击从“欺骗人类”向“操纵AI逻辑”的演进 字符串相等 地址比较 内容比较 ‌ 零宽不连字, 不可见字符 ch.isprintable() purge会移除配置文件,而remove仅删除程序文件。 c# 异步 同步 请求 生成式引擎优化 Generative Engine Optimization GEO 内容优化策略 python lua 传值 传址 用AI原生的思维重新构建产品
go java python dart 内存模型区别
papering · 2026-04-09 · via 博客园 - papering

python函数入参列表list,列表list被修改了;

go传入数组,传入的是副本,原数组没有被修改; 开销: 高(拷贝整个数组)

go传入切片,传入的是副本,原切片被修改;开销: 低(拷贝24字节)  (切片的底层数组共享)

package main

import "fmt"

func f(a [3]int) {
a[1] = 123
}
func g(s []int){
s[1]=123
}

func main() {
arr := [3]int{1, 2, 3}
fmt.Println(arr)
f(arr)
fmt.Println(arr)

var slice1 []int
slice1 = []int{1, 2, 3}
fmt.Println(slice1)
g(slice1)
fmt.Println(slice1)
}

[1 2 3]
[1 2 3]
[1 2 3]
[1 123 3]

python

a=[1,2,3]
def f(b):
b[1]=123

print(a)
f(a)
print(a)

[1, 2, 3]
[1, 123, 3]

数组内存分布

var arr [5]int{1, 2, 3, 4, 5}

内存地址: 0x100 0x108 0x110 0x118 0x120
┌──────┬──────┬──────┬──────┬──────┐
│ 1 │ 2 │ 3 │ 4 │ 5 │
└──────┴──────┴──────┴──────┴──────┘

大小: 5 * 8 = 40 bytes (int64)

切片内存分布

slice := []int{1, 2, 3, 4, 5}

Slice Header (24 bytes on 64-bit):
┌─────────────────┐
│ array: 0x100 │ ← 指针(8 bytes)
├─────────────────┤
│ len: 5 │ ← 长度(8 bytes)
├─────────────────┤
│ cap: 5 │ ← 容量(8 bytes)
└─────────────────┘
↓ 指向
底层数组:
┌──────┬──────┬──────┬──────┬──────┐
│ 1 │ 2 │ 3 │ 4 │ 5 │
└──────┴──────┴──────┴──────┴──────┘

特性 GoJava (JVM)PythonDart
内存管理 自动 GC(三色标记法) 自动 GC(分代收集) 引用计数 + 循环垃圾检测 自动 GC(分代收集)
并发模型 CSP 模型(通过 channel 通信) 共享内存(线程+锁) GIL 锁(同一时刻单线程) Isolate 隔离(不共享内存)
内存安全 编译期逃逸分析 强类型,基本只在堆分配 动态类型,全对象化 严格类型,AOT/JIT 混合

Go: 轻量级与通信

Go 的设计哲学是“不要通过共享内存来通信,而要通过通信来共享内存”。 

  • 逃逸分析:编译器会自动决定变量分配在栈(Stack)还是堆(Heap)上。如果变量在函数结束后不再使用,优先放在栈上,这极大降低了 GC 压力。
  • 并发模型:利用 Goroutine,通过 channel 传递数据,避免了传统多线程中复杂的锁竞争问题。

Java: 经典的共享模型

Java 内存模型(JMM)围绕多线程共享堆内存展开。 

  • 分代收集:将内存分为新生代、老年代,针对不同寿命的对象使用不同的 GC 算法。
  • 内存屏障:通过 volatile 等关键字确保多线程下的可见性和有序性,依赖锁机制(synchronized/Lock)来保证线程安全。 

Python: 解释器的限制

Python 的内存管理深受 GIL(全局解释器锁) 的影响。 

  • 引用计数:对象被引用一次计数加 1,归零即销毁。这是最直接的内存释放方式。
  • 并发限制:由于 GIL 的存在,多个 CPU 核心无法同时执行 Python 字节码,因此 Python 主要是通过多进程或 asyncio 来处理并发,而非共享内存的多线程。 

Dart: 隔离的 Isolate

Dart 在 Flutter 等场景下的内存表现优异,其核心在于 Isolate。 

  • 无共享内存:每个 Isolate 拥有自己独立的堆和栈,不同 Isolate 之间不能直接访问彼此内存,只能通过消息传递通信。这种设计彻底消除了加锁带来的开销和死锁风险。
  • 分代假说:针对 UI 框架中对象“瞬时创建、瞬时销毁”的特点,Dart 拥有极其高效的新生代清理机制。 

Python 进程
├─ Main Thread (主线程)
├─ Thread 1 (子线程)
├─ Thread 2 (子线程)
└─ ...

所有线程共享:
- 同一进程内存空间
- 全局变量
- 堆内存
- 文件描述符

每个线程独立:
- 栈空间
- 寄存器状态
- 程序计数器

# GIL 保证同一时刻只有一个线程执行 Python 字节码
# 这是 CPython 的设计决策,简化了内存管理

执行流程:
Thread 1: [获取 GIL] → [执行 100 个字节码] → [释放 GIL] → [等待]
Thread 2: [等待] → [获取 GIL] → [执行 100 个字节码] → [释放 GIL] → [等待]
Thread 1: [等待] → [获取 GIL] → ...

Python 有 GIL(全局解释器锁)限制,同一时刻只有一个线程执行字节码。而 Java 没有 GIL 限制,可以实现真正的并行执行。

Python: Thread 1 [获取GIL] → 执行 → [释放GIL] → Thread 2 [等待] → [获取GIL] → ...
Java: Thread 1 ████████ 真正并行执行
Thread 2 ████████ 真正并行执行
Thread 3 ████████ 真正并行执行

        M:N 模型(Goroutine : Machine : Processor)

G (Goroutine) → M (OS Thread) → P (Processor) → CPU Core

GMP 调度模型:
┌─────────────────────────────────┐
│ G1 G2 G3 (Goroutines) │ ← 用户态,轻量级
└──────┬──┬──┬───────────────────┘
│ │ │
┌──────▼──▼──▼───────────────────┐
│ M1 M2 (OS Threads) │ ← 内核线程
└──────┬──┬──────────────────────┘
│ │
┌──────▼──▼──────────────────────┐
│ P1 P2 (Processors) │ ← 逻辑处理器
└──────┬──┬──────────────────────┘
│ │
▼ ▼
CPU1 CPU2

Java Thread
Go Goroutine
创建时间
~1ms
~2μs
内存占用
1MB
2KB (动态增长)
切换成本
~1-10μs
~200ns
最大数量
~几千
~百万
调度方式
抢占式(OS)
协作式 + 抢占式

特性
Java
Go
对象分配
优先在 Eden 区
逃逸分析决定栈或堆
栈管理
固定大小(可配置)
动态增长(2KB 起步)
线程栈
1MB 默认
2KB 默认(goroutine)
内存区域
复杂(新生代、老年代等)
简单(堆 + 栈)

信号量

场景 2:API 速率限制

import threading
import time
import requests

class RateLimiter:
"""API 速率限制器"""
def __init__(self, max_requests=10, time_window=60):
self.semaphore = threading.Semaphore(max_requests)
self.time_window = time_window

def make_request(self, url):
"""发送 API 请求"""
self.semaphore.acquire()
try:
print(f"发送请求到: {url}")
response = requests.get(url, timeout=5)
return response
finally:
# 在时间窗口后释放信号量
threading.Timer(self.time_window, self.semaphore.release).start()

# 限制每秒最多 5 个请求
limiter = RateLimiter(max_requests=5, time_window=1)

def fetch_data(item_id):
url = f"https://api.example.com/items/{item_id}"
response = limiter.make_request(url)
print(f"获取到数据: {item_id}")

threads = [threading.Thread(target=fetch_data, args=(i,)) for i in range(20)]
for t in threads:
t.start()

这段代码在逻辑思路上是可行的,但在高并发或严苛的生产环境下存在几个隐患。它能起到“限流”的作用,但并不是一个标准的“令牌桶”或“漏桶”算法实现。

以下是详细分析:

1. 核心优点

  • 非阻塞式释放:使用 threading.Timer 在后台异步释放信号量,不会阻塞当前请求线程,设计很巧妙。
  • 基础限流有效:对于低频率、少量的并发请求,它能强制维持“每 time_window 秒最多 max_requests 个请求”的限制。

2. 潜在隐患(Bug 与性能)

  • 线程爆炸风险:
    每次请求都会创建一个新的 threading.Timer 线程。如果你要在 1 分钟内处理 1000 个请求,就会额外创建 1000 个瞬时线程来负责 release。这会显著消耗系统资源,甚至触发操作系统的线程数限制。
  • 误差累积:
    Python 的 Timer 并不保证精确的纳秒级触发。在高负载下,Timer 的启动和执行会有延迟,导致实际的限流频率比预设的要慢。
  • 信号量计数值溢出:
    由于使用的是 Semaphore 而非 BoundedSemaphore,如果代码逻辑在某些异常情况下多次触发了释放逻辑,信号量的计数器可能会超过初始值,导致限流失效。
  • 异常处理不足:
    如果 requests.get 抛出异常(如超时或网络错误),finally 块依然会启动 Timer。这本身没错,但要注意 API 报错时是否仍计入速率限制。

3. 改进建议

如果你追求代码简洁且并发量不大,可以改用更轻量的方式。如果并发量大,建议使用专门的限流库。

轻量级改进版本(减少线程消耗):

import time
from collections import deque

class RateLimiter:
    def __init__(self, max_requests=5, time_window=1):
        self.max_requests = max_requests
        self.time_window = time_window
        self.requests_times = deque() # 记录请求时间戳
        self.lock = threading.Lock()

    def make_request(self, url):
        with self.lock:
            current_time = time.time()
            # 移除时间窗口之外的旧记录
            while self.requests_times and self.requests_times[0] <= current_time - self.time_window:
                self.requests_times.popleft()
            
            # 如果达到上限,则等待(或者直接报错/重试)
            if len(self.requests_times) >= self.max_requests:
                sleep_time = self.requests_times[0] + self.time_window - current_time
                time.sleep(max(0, sleep_time))
            
            self.requests_times.append(time.time())
            
        # 执行真正的请求
        print(f"发送请求: {url}")
        return requests.get(url)

总结

你的代码作为学习信号量的高级用法非常棒,但在生产环境中:

  1. 如果请求量大,建议使用 ratelimittenacity 等成熟库。
  2. 如果是分布式环境,必须使用 Redis 实现信号量或令牌桶。

主要问题
Timer 线程泄漏:每次请求都创建新 Timer,20 个请求会创建 20 个后台线程
信号量释放时机不准确:Timer 是异步的,可能导致并发控制不精确
缺少错误处理:网络请求失败时没有重试机制
无法优雅关闭:没有清理资源的方法
时间窗口实现不合理:应该用滑动窗口而非固定延迟

 import threading
import time
import requests
from collections import deque

class RateLimiter:
"""API 速率限制器 - 基于滑动窗口算法"""

def __init__(self, max_requests=10, time_window=60):
self.max_requests = max_requests
self.time_window = time_window
self.semaphore = threading.Semaphore(max_requests)
self.request_times = deque()
self.lock = threading.Lock()
self._shutdown = False

def _clean_old_requests(self):
"""清理超出时间窗口的请求记录"""
now = time.time()
cutoff = now - self.time_window
while self.request_times and self.request_times[0] < cutoff:
self.request_times.popleft()

def _wait_for_slot(self):
"""等待可用的请求槽位"""
while not self._shutdown:
with self.lock:
self._clean_old_requests()
if len(self.request_times) < self.max_requests:
self.request_times.append(time.time())
return True

# 计算需要等待的时间
with self.lock:
if self.request_times:
oldest = self.request_times[0]
wait_time = self.time_window - (time.time() - oldest) + 0.1
else:
wait_time = 0.1

if wait_time > 0:
time.sleep(min(wait_time, 1.0))

raise RuntimeError("RateLimiter has been shut down")

def make_request(self, url, max_retries=3, timeout=5):
"""发送 API 请求,带重试机制"""
self._wait_for_slot()

for attempt in range(max_retries):
try:
print(f"[尝试 {attempt + 1}/{max_retries}] 发送请求到: {url}")
response = requests.get(url, timeout=timeout)
response.raise_for_status()

print(f"✓ 请求成功: {url} (状态码: {response.status_code})")
return response

except requests.exceptions.Timeout:
print(f"⚠ 请求超时: {url}")
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)

except requests.exceptions.ConnectionError as e:
print(f"⚠ 连接错误: {url} - {e}")
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)

except requests.exceptions.HTTPError as e:
print(f"✗ HTTP 错误: {url} - {e}")
raise

return None

def shutdown(self):
"""优雅关闭速率限制器"""
self._shutdown = True
print("RateLimiter 已关闭")

# 限制每秒最多 5 个请求
limiter = RateLimiter(max_requests=5, time_window=1)

def fetch_data(item_id):
"""获取数据的 worker 函数"""
try:
url = f"https://api.example.com/items/{item_id}"
response = limiter.make_request(url)
if response:
print(f"✅ 获取到数据: {item_id}, 大小: {len(response.content)} bytes")
except Exception as e:
print(f"❌ 获取数据失败 {item_id}: {e}")

try:
threads = [threading.Thread(target=fetch_data, args=(i,), daemon=True)
for i in range(20)]

for t in threads:
t.start()

for t in threads:
t.join(timeout=30)

print("\n所有请求完成")

finally:
limiter.shutdown()


1. 滑动窗口算法替代 Timer

# ❌ 原代码:创建大量 Timer 线程
threading.Timer(self.time_window, self.semaphore.release).start()

# ✅ 优化后:使用 deque 记录请求时间,自动清理过期记录
self.request_times = deque()
self._clean_old_requests() # 移除超出时间窗口的记录

优势:
不会创建额外线程,避免资源泄漏
更精确的速率控制
支持动态调整时间窗口

2. 指数退避重试机制

for attempt in range(max_retries):
try:
response = requests.get(url, timeout=timeout)
response.raise_for_status()
return response
except requests.exceptions.Timeout:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # 1s, 2s, 4s...

3. 优雅关闭支持
def shutdown(self):
self._shutdown = True

# 使用时
try:
# ... 执行任务 ...
finally:
limiter.shutdown() # 确保资源清理

4. 守护线程 + 超时控制
threads = [threading.Thread(target=fetch_data, args=(i,), daemon=True)
for i in range(20)]

for t in threads:
t.join(timeout=30) # 最多等待 30 秒

更高级的版本(使用令牌桶算法)

import threading
import time
import requests

class TokenBucketRateLimiter:
"""基于令牌桶算法的速率限制器"""

def __init__(self, rate=5, capacity=10):
"""
:param rate: 每秒生成的令牌数
:param capacity: 桶的最大容量
"""
self.rate = rate
self.capacity = capacity
self.tokens = capacity
self.last_refill = time.time()
self.lock = threading.Lock()
self._shutdown = False

def _refill(self):
"""补充令牌"""
now = time.time()
elapsed = now - self.last_refill
new_tokens = elapsed * self.rate
self.tokens = min(self.capacity, self.tokens + new_tokens)
self.last_refill = now

def acquire(self, timeout=None):
"""获取令牌"""
deadline = time.time() + timeout if timeout else None

while not self._shutdown:
with self.lock:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True

if deadline and time.time() > deadline:
return False

time.sleep(0.1)

raise RuntimeError("TokenBucketRateLimiter has been shut down")

def make_request(self, url, timeout=5, max_retries=3):
"""发送请求"""
if not self.acquire(timeout=10):
raise TimeoutError("等待令牌超时")

for attempt in range(max_retries):
try:
response = requests.get(url, timeout=timeout)
response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
if attempt == max_retries - 1:
raise
time.sleep(min(2 ** attempt, 10))

def shutdown(self):
self._shutdown = True

# 使用:每秒 5 个请求,峰值可达 10 个
limiter = TokenBucketRateLimiter(rate=5, capacity=10)

def fetch_data(item_id):
try:
url = f"https://api.example.com/items/{item_id}"
response = limiter.make_request(url)
print(f"✅ {item_id}: {len(response.content)} bytes")
except Exception as e:
print(f"❌ {item_id}: {e}")

threads = [threading.Thread(target=fetch_data, args=(i,), daemon=True)
for i in range(20)]

try:
for t in threads:
t.start()
for t in threads:
t.join(timeout=30)
finally:
limiter.shutdown()

image