1. thread_local! 导致跨线程缓存不可见(主要问题)

kline_py.rs 中 BSP_CACHE、KLINE_IDENTITY、BAR_IDENTITY 使用了 thread_local!。主线程调用 识别买卖点() →
  买卖点信息.add() 写入的是主线程的 PySet,backtrader 策略线程读取的是自己线程独立的空 PySet。

  修复:将三个缓存从 thread_local! 改为全局 static + std::sync::LazyLock<RwLock<HashMap<...>>>

  影响分析

  这些身份缓存的影响与 KLINE_IDENTITY/BAR_IDENTITY 不同——它们只影响 Python 对象身份(is
  比较),不直接影响数据内容(因为 Rust 数据通过 Arc 共享,读写都是同一份)。

  具体后果:
  - 不同线程访问同一个 Rust Arc 会得到不同的 Python wrapper 对象
  - a is b 跨线程比较返回 False,哪怕它们包装同一个底层 Rust 对象
  - 每个线程维护一份独立缓存,内存浪费(不过 wrapper 很小)
This commit is contained in:
YuWuKunCheng
2026-05-30 22:15:09 +08:00
parent 15dc44e8e0
commit b7c4e60420
12 changed files with 327 additions and 309 deletions
+82 -85
View File
@@ -31,26 +31,28 @@ use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::RwLock;
thread_local! {
static HUB_IDENTITY: RwLock<HashMap<usize, Py<Py>>> = RwLock::new(HashMap::new());
}
// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
static HUB_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
pub(crate) fn hub_to_py(
py: Python<'_>, inner: Arc<chanlun::algorithm::hub::>
) -> Py<Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
HUB_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = HUB_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
HUB_IDENTITY.with(|c| {
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
});
HUB_IDENTITY
.write()
.unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, Py { inner }).unwrap();
HUB_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
HUB_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
obj
}
@@ -362,13 +364,13 @@ impl 笔Py {
: Vec<Py<线Py>>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> Option<线Py> {
) -> Option<Py<线Py>> {
let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect();
chanlun::algorithm::bi::::(&bi_list, &.borrow().inner)
.map(|inner| 线Py { inner })
.map(|inner| dashed_to_py(py, inner))
}
#[classmethod]
@@ -378,13 +380,13 @@ impl 笔Py {
: Vec<Py<线Py>>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> Option<线Py> {
) -> Option<Py<线Py>> {
let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect();
chanlun::algorithm::bi::::(&bi_list, &.borrow().inner)
.map(|inner| 线Py { inner })
.map(|inner| dashed_to_py(py, inner))
}
#[classmethod]
@@ -396,13 +398,13 @@ impl 笔Py {
K: &Bound<'_, crate::kline_py::K线Py>,
: i64,
py: Python<'_>,
) -> Option<线Py> {
) -> Option<Py<线Py>> {
let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect();
chanlun::algorithm::bi::::K找笔(&bi_list, &K.borrow().inner, )
.map(|inner| 线Py { inner })
.map(|inner| dashed_to_py(py, inner))
}
#[classmethod]
@@ -501,7 +503,7 @@ impl 笔Py {
) -> bool {
let obs = .borrow();
let obs_ref = obs.obs();
chanlun::algorithm::bi::::(&.borrow().inner, &*obs_ref)
chanlun::algorithm::bi::::(&.borrow().inner, &obs_ref)
}
#[classmethod]
@@ -510,12 +512,13 @@ impl 笔Py {
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
: &Bound<'_, Py>,
) -> Vec<线Py> {
py: Python<'_>,
) -> Vec<Py<线Py>> {
let obs = .borrow();
let obs_ref = obs.obs();
chanlun::algorithm::bi::::(&.borrow().inner, &*obs_ref)
chanlun::algorithm::bi::::(&.borrow().inner, &obs_ref)
.into_iter()
.map(|d| 线Py { inner: Arc::new(d) })
.map(|d| dashed_to_py(py, Arc::new(d)))
.collect()
}
@@ -529,7 +532,7 @@ impl 笔Py {
) -> Vec<Py<K线Py>> {
let obs = .borrow();
let obs_ref = obs.obs();
chanlun::algorithm::bi::::(&.borrow().inner, &*obs_ref)
chanlun::algorithm::bi::::(&.borrow().inner, &obs_ref)
.into_iter()
.map(|ck| chan_kline_to_py(py, ck))
.collect()
@@ -569,8 +572,8 @@ impl 线段Py {
: &Bound<'_, 线Py>,
) -> PyResult<()> {
let bi_rc = Arc::clone(&.borrow().inner);
let mut ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::线(&mut ref_mut.inner, bi_rc);
let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::线(&ref_mut.inner, bi_rc);
Ok(())
}
@@ -582,9 +585,9 @@ impl 线段Py {
: &Bound<'_, Py>,
: u32,
) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(
&mut ref_mut.inner,
&ref_mut.inner,
&Arc::clone(&.borrow().inner),
,
);
@@ -594,8 +597,8 @@ impl 线段Py {
#[classmethod]
/// 武终
fn (_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>, : u32) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, );
let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&ref_mut.inner, );
Ok(())
}
@@ -607,12 +610,12 @@ impl 线段Py {
: Vec<Py<线Py>>,
py: Python<'_>,
) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
let ref_mut = .borrow_mut();
let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &rc_list);
chanlun::algorithm::segment::线::(&ref_mut.inner, &rc_list);
Ok(())
}
@@ -624,12 +627,12 @@ impl 线段Py {
: Vec<Py<线Py>>,
py: Python<'_>,
) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
let ref_mut = .borrow_mut();
let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &rc_list);
chanlun::algorithm::segment::线::(&ref_mut.inner, &rc_list);
Ok(())
}
@@ -692,7 +695,7 @@ impl 线段Py {
let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线>>> = if .is_none()
{
vec![]
} else if let Ok(list) = .downcast::<pyo3::types::PyList>() {
} else if let Ok(list) = .cast::<pyo3::types::PyList>() {
let mut result = Vec::with_capacity(list.len());
for item in list.iter() {
if item.is_none() {
@@ -708,8 +711,8 @@ impl 线段Py {
"序列 必须是 list 或 None",
));
};
let mut ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, seq, );
let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&ref_mut.inner, seq, );
Ok(())
}
@@ -721,22 +724,26 @@ impl 线段Py {
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
let ref_mut = .borrow_mut();
let config = .borrow().to_rust_config(py)?;
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &config);
chanlun::algorithm::segment::线::(&ref_mut.inner, &config);
Ok(())
}
#[classmethod]
/// 查找贯穿伤
fn 穿(_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>) -> Option<线Py> {
fn 穿(
_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>
) -> Option<Py<线Py>> {
let py = _cls.py();
chanlun::algorithm::segment::线::穿(&.borrow().inner)
.map(|inner| 线Py { inner })
.map(|inner| dashed_to_py(py, inner))
}
#[classmethod]
#[pyo3(signature = (段, 所属中枢 = None))]
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
#[allow(clippy::type_complexity)]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -761,19 +768,10 @@ impl 线段Py {
} else {
chanlun::algorithm::segment::线::(&borrowed.inner, None)
};
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::线>>| -> PyResult<Vec<Py<线Py>>> {
let mut result = Vec::new();
for x in v {
result.push(Py::new(py, 线Py { inner: x })?);
}
Ok(result)
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::线>>| -> Vec<Py<线Py>> {
v.into_iter().map(|x| dashed_to_py(py, x)).collect()
};
Ok((
wrap(a)?,
wrap(b)?,
wrap(c)?,
d.map(|x| Py::new(py, 线Py { inner: x })).transpose()?,
))
Ok((wrap(a), wrap(b), wrap(c), d.map(|x| dashed_to_py(py, x))))
}
#[classmethod]
@@ -784,9 +782,9 @@ impl 线段Py {
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
let ref_mut = .borrow_mut();
let config = .borrow().to_rust_config(py)?;
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &config);
chanlun::algorithm::segment::线::(&ref_mut.inner, &config);
Ok(())
}
@@ -798,16 +796,14 @@ impl 线段Py {
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<(Py<PyAny>, Py<PyAny>, Py<PyAny>)> {
let mut ref_mut = .borrow_mut();
let ref_mut = .borrow_mut();
let config = .borrow().to_rust_config(py)?;
let (a, b, c) = chanlun::algorithm::segment::线::(
&mut ref_mut.inner,
&config,
);
let (a, b, c) =
chanlun::algorithm::segment::线::(&ref_mut.inner, &config);
let pk_list = |v: Vec<Arc<chanlun::algorithm::hub::>>| -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in v {
list.append(Py::new(py, Py { inner: h })?)?;
list.append(hub_to_py(py, h))?;
}
Ok(list.into())
};
@@ -818,7 +814,7 @@ impl 线段Py {
/// 内部方法:向线段序列添加新线段
fn _添加线段(
_cls: &Bound<'_, PyType>,
线: &Bound<'_, PyAny>,
_线段序列: &Bound<'_, PyAny>,
线: &Bound<'_, 线Py>,
: &Bound<'_, Py>,
: String,
@@ -839,12 +835,12 @@ impl 线段Py {
/// 内部方法:从线段序列弹出最后一个线段
fn _弹出线段(
_cls: &Bound<'_, PyType>,
线: &Bound<'_, PyAny>,
_线段序列: &Bound<'_, PyAny>,
线: &Bound<'_, 线Py>,
: &Bound<'_, Py>,
: String,
py: Python<'_>,
) -> PyResult<Option<线Py>> {
) -> PyResult<Option<Py<线Py>>> {
let config = .borrow().to_rust_config(py)?;
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::线>> = vec![];
let result = chanlun::algorithm::segment::线::_弹出线段(
@@ -853,7 +849,7 @@ impl 线段Py {
&config,
,
);
Ok(result.map(|inner| 线Py { inner }))
Ok(result.map(|inner| dashed_to_py(py, inner)))
}
#[classmethod]
@@ -1007,7 +1003,7 @@ impl 线段Py {
线: &Bound<'_, 线Py>,
: u32,
py: Python<'_>,
) -> PyResult<Option<线Py>> {
) -> PyResult<Option<Py<线Py>>> {
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::线>> = 线
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
@@ -1017,7 +1013,7 @@ impl 线段Py {
&Arc::clone(&线.borrow().inner),
,
);
Ok(result.map(|inner| 线Py { inner }))
Ok(result.map(|inner| dashed_to_py(py, inner)))
}
#[classmethod]
@@ -1053,7 +1049,7 @@ impl 线段Py {
let obs_ref = obs.obs();
chanlun::algorithm::segment::线::线(
&.borrow().inner,
&*obs_ref,
&obs_ref,
)
}
@@ -1063,12 +1059,13 @@ impl 线段Py {
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
: &Bound<'_, Py>,
) -> Vec<线Py> {
py: Python<'_>,
) -> Vec<Py<线Py>> {
let obs = .borrow();
let obs_ref = obs.obs();
chanlun::algorithm::segment::线::(&.borrow().inner, &*obs_ref)
chanlun::algorithm::segment::线::(&.borrow().inner, &obs_ref)
.into_iter()
.map(|d| 线Py { inner: Arc::new(d) })
.map(|d| dashed_to_py(py, Arc::new(d)))
.collect()
}
@@ -1082,7 +1079,7 @@ impl 线段Py {
) -> Vec<Py<K线Py>> {
let obs = .borrow();
let obs_ref = obs.obs();
chanlun::algorithm::segment::线::(&.borrow().inner, &*obs_ref)
chanlun::algorithm::segment::线::(&.borrow().inner, &obs_ref)
.into_iter()
.map(|ck| chan_kline_to_py(py, ck))
.collect()
@@ -1264,7 +1261,7 @@ impl 中枢Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in self.inner.() {
list.append(线Py { inner: d })?;
list.append(dashed_to_py(py, d))?;
}
Ok(list.into())
}
@@ -1381,16 +1378,15 @@ impl 中枢Py {
: &Bound<'_, 线Py>,
: i64,
: &str,
) -> Self {
Self {
inner: Arc::new(chanlun::algorithm::hub::::(
Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner),
,
,
)),
}
) -> Py<Py> {
let inner = Arc::new(chanlun::algorithm::hub::::(
Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner),
,
,
));
hub_to_py(_cls.py(), inner)
}
#[classmethod]
@@ -1401,7 +1397,7 @@ impl 中枢Py {
: &Bound<'_, Py>,
: &str,
py: Python<'_>,
) -> Option<Self> {
) -> Option<Py<Py>> {
let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> = 线
.iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
@@ -1411,7 +1407,7 @@ impl 中枢Py {
.borrow().inner,
,
)
.map(|inner| Self { inner })
.map(|inner| hub_to_py(py, inner))
}
#[classmethod]
@@ -1421,8 +1417,9 @@ impl 中枢Py {
: &Bound<'_, PyAny>,
: &Bound<'_, Self>,
) -> PyResult<()> {
let py = .py();
let inner = Arc::clone(&.borrow().inner);
let wrapper = Py::new(.py(), Self { inner })?;
let wrapper = hub_to_py(py, inner);
.call_method1("append", (wrapper,))?;
Ok(())
}
@@ -1432,7 +1429,7 @@ impl 中枢Py {
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>,
: &Bound<'_, Self>,
_待弹出中枢: &Bound<'_, Self>,
) -> PyResult<Option<Self>> {
let result = .call_method1("pop", ())?;
if result.is_none() {
+18 -28
View File
@@ -28,14 +28,12 @@ use std::sync::RwLock;
use crate::algorithm_py::hub_to_py;
use crate::kline_py::bar_to_py;
use crate::structure_py::{dashed_to_py, fractal_to_py};
use crate::structure_py::{dashed_to_py, fractal_to_py, Py};
use std::collections::HashMap;
use std::sync::Arc;
use crate::algorithm_py::Py;
use crate::config_py::Py;
use crate::kline_py::{K线Py, K线Py};
use crate::structure_py::{Py, 线Py};
use crate::types_py::Py;
// ========== 基础买卖点 ==========
@@ -100,10 +98,8 @@ impl 基础买卖点Py {
}
#[getter]
fn (&self) -> Py {
Py {
inner: Arc::clone(&self.inner.),
}
fn (&self, py: Python<'_>) -> Py<Py> {
fractal_to_py(py, Arc::clone(&self.inner.))
}
#[getter]
@@ -576,13 +572,13 @@ impl 观察者Py {
let obs = me.obs();
(obs..clone(), obs.)
};
let kline = K线Py {
inner: Arc::new(chanlun::kline::bar::K线::K(
let kline = bar_to_py(
slf.py(),
Arc::new(chanlun::kline::bar::K线::K(
&, , , , , , , 0, ,
)),
};
let kline_py = Py::new(slf.py(), kline)?;
slf.call_method1("增加原始K线", (kline_py,))?;
);
slf.call_method1("增加原始K线", (kline,))?;
Ok(())
}
@@ -860,11 +856,11 @@ impl K线合成器Py {
&mut self,
K: &Bound<'_, K线Py>,
py: Python<'_>,
) -> PyResult<Vec<(i64, K线Py)>> {
) -> PyResult<Vec<(i64, Py<K线Py>)>> {
let results = self.inner.K线((*K.borrow().inner).clone());
Ok(results
.into_iter()
.map(|(, k)| (, K线Py { inner: Arc::new(k) }))
.map(|(, k)| (, bar_to_py(py, Arc::new(k))))
.collect())
}
@@ -877,7 +873,8 @@ impl K线合成器Py {
: f64,
: f64,
: f64,
) -> Vec<(i64, K线Py)> {
py: Python<'_>,
) -> Vec<(i64, Py<K线Py>)> {
let min_cycle = self.inner..iter().copied().min().unwrap_or(1);
let k = chanlun::kline::bar::K线::K(
&self.inner.,
@@ -893,22 +890,15 @@ impl K线合成器Py {
let results = self.inner.K线(k);
results
.into_iter()
.map(|(, k2)| {
(
,
K线Py {
inner: Arc::new(k2),
},
)
})
.map(|(, k2)| (, bar_to_py(py, Arc::new(k2))))
.collect()
}
/// 获取指定周期当前正在合成的K线
fn K线(&self, : i64) -> Option<K线Py> {
self.inner.K线().map(|k| K线Py {
inner: Arc::new(k.clone()),
})
fn K线(&self, : i64, py: Python<'_>) -> Option<Py<K线Py>> {
self.inner
.K线()
.map(|k| bar_to_py(py, Arc::new(k.clone())))
}
#[getter]
@@ -957,7 +947,7 @@ impl 立体分析器Py {
};
let cfg_map: Option<HashMap<i64, chanlun::config::>> = match {
Some(dict_any) => {
let dict = dict_any.downcast::<pyo3::types::PyDict>()?;
let dict = dict_any.cast::<pyo3::types::PyDict>()?;
let mut map = HashMap::new();
for (key, value) in dict.iter() {
let period: i64 = key.extract()?;
+6 -2
View File
@@ -239,7 +239,7 @@ impl 缠论配置Py {
) -> PyResult<Py<PyDict>> {
let py = .py();
let result = PyDict::new(py);
if let Ok(default_dict) = .downcast::<PyDict>() {
if let Ok(default_dict) = .cast::<PyDict>() {
for (key, value) in default_dict.iter() {
if .contains(&key)? {
result.set_item(key.clone(), .get_item(&key)?)?;
@@ -252,6 +252,7 @@ impl 缠论配置Py {
}
/// 比较当前配置与另一个配置的差异
#[allow(clippy::type_complexity)]
fn (
&self,
py: Python<'_>,
@@ -290,7 +291,10 @@ impl 缠论配置Py {
Ok(Self { fields })
}
pub(crate) fn to_rust_config(&self, py: Python<'_>) -> PyResult<chanlun::config::> {
pub(crate) fn to_rust_config(
&self,
_py: Python<'_>,
) -> PyResult<chanlun::config::> {
dict_to_rust_config(&self.fields)
}
+40 -31
View File
@@ -355,17 +355,18 @@ impl 缠论K线Py {
}
}
thread_local! {
/// 对象标识缓存:Rc 地址 → 规范 Python 对象
/// 确保同一底层 Rc 指针在 Python 侧始终映射到同一 PyObject
/// 对象标识缓存:Arc 地址 → 规范 Python 对象
/// 确保同一底层 Arc 指针在 Python 侧始终映射到同一 PyObject
/// 使用全局 static 而非 thread_local!,保证跨线程对象标识和买卖点信息一致性
static BAR_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<K线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static BAR_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new());
static KLINE_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<K线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static KLINE_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new());
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
static BSP_CACHE: RwLock<HashMap<usize, Py<pyo3::types::PySet>>> = RwLock::new(HashMap::new());
}
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
static BSP_CACHE: std::sync::LazyLock<RwLock<HashMap<usize, Py<pyo3::types::PySet>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
pub(crate) fn bar_to_py(
@@ -373,15 +374,16 @@ pub(crate) fn bar_to_py(
inner: std::sync::Arc<chanlun::kline::bar::K线>,
) -> Py<K线Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
BAR_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = BAR_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
let obj = Py::new(py, K线Py { inner }).unwrap();
BAR_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
BAR_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
obj
}
@@ -391,15 +393,19 @@ pub(crate) fn chan_kline_to_py(
inner: std::sync::Arc<chanlun::kline::chan_kline::K线>,
) -> Py<K线Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
KLINE_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = KLINE_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
let obj = Py::new(py, K线Py::from_rc(inner)).unwrap();
KLINE_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
KLINE_IDENTITY
.write()
.unwrap()
.insert(key, obj.clone_ref(py));
obj
}
@@ -534,17 +540,18 @@ impl 缠论K线Py {
// 复制买卖点信息到镜像
let src_key = Arc::as_ptr(&self.inner) as usize;
let dst_key = Arc::as_ptr(&mirror.inner) as usize;
let cached_src =
BSP_CACHE.with(|c| c.read().unwrap().get(&src_key).map(|p| p.clone_ref(py)));
let cached_src = BSP_CACHE
.read()
.unwrap()
.get(&src_key)
.map(|p| p.clone_ref(py));
if let Some(cached_src) = cached_src {
if let Ok(new_set) = pyo3::types::PySet::empty(py) {
for item in cached_src.bind(py).iter() {
let _ = new_set.add(item);
}
let py_set: Py<pyo3::types::PySet> = new_set.into();
BSP_CACHE.with(|c| {
c.write().unwrap().insert(dst_key, py_set);
});
BSP_CACHE.write().unwrap().insert(dst_key, py_set);
}
}
mirror
@@ -572,18 +579,20 @@ impl 缠论K线Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let key = Arc::as_ptr(&self.inner) as usize;
// 检查全局缓存
let cached = BSP_CACHE.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)));
let cached = BSP_CACHE.read().unwrap().get(&key).map(|p| p.clone_ref(py));
if let Some(set) = cached {
return Ok(set.into_any());
}
// 创建新的 PySet 并存入全局缓存
let set = pyo3::types::PySet::empty(py)?;
BSP_CACHE.with(|c| {
c.write().unwrap().insert(key, set.into());
});
BSP_CACHE.write().unwrap().insert(key, set.into());
// 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读)
Ok(BSP_CACHE
.with(|c| c.read().unwrap().get(&key).unwrap().clone_ref(py))
.read()
.unwrap()
.get(&key)
.unwrap()
.clone_ref(py)
.into_any())
}
@@ -642,13 +651,13 @@ impl 缠论K线Py {
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<(Option<Py<Self>>, Option<String>)> {
let mut ck_inner = (*K.borrow().inner).clone();
let ck_inner = (*K.borrow().inner).clone();
let config = .borrow().to_rust_config(py)?;
let prev_ref = K.map(|prev| prev.borrow());
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
let (result, mode) = chanlun::kline::chan_kline::K线::(
prev_inner,
&mut ck_inner,
&ck_inner,
&K.borrow().inner,
&config,
);
+2
View File
@@ -22,6 +22,8 @@
* SOFTWARE.
*/
#![allow(non_snake_case, clippy::too_many_arguments)]
use pyo3::prelude::*;
use std::sync::atomic::Ordering;
+73 -63
View File
@@ -29,37 +29,45 @@ use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::RwLock;
use crate::algorithm_py::{hub_to_py, Py};
use crate::algorithm_py::hub_to_py;
use crate::config_py::Py;
use crate::kline_py::{K线Py, K线Py};
use crate::kline_py::{bar_to_py, K线Py, K线Py};
// ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ----
thread_local! {
static FRACTAL_IDENTITY: RwLock<HashMap<usize, Py<Py>>> = RwLock::new(HashMap::new());
static DASHED_IDENTITY: RwLock<HashMap<usize, Py<线Py>>> = RwLock::new(HashMap::new());
static SEGFEAT_IDENTITY: RwLock<HashMap<usize, Py<线Py>>> = RwLock::new(HashMap::new());
static FEATFRAC_IDENTITY: RwLock<HashMap<usize, Py<Py>>> = RwLock::new(HashMap::new());
}
// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
static FRACTAL_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static DASHED_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static SEGFEAT_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static FEATFRAC_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
pub(crate) fn fractal_to_py(
py: Python<'_>,
inner: Arc<chanlun::structure::fractal_obj::>,
) -> Py<Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
FRACTAL_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = FRACTAL_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
// 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用)
FRACTAL_IDENTITY.with(|c| {
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
});
FRACTAL_IDENTITY
.write()
.unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, Py { inner }).unwrap();
FRACTAL_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
FRACTAL_IDENTITY
.write()
.unwrap()
.insert(key, obj.clone_ref(py));
obj
}
@@ -68,18 +76,23 @@ pub(crate) fn dashed_to_py(
inner: Arc<chanlun::structure::dash_line::线>,
) -> Py<线Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
DASHED_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = DASHED_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
DASHED_IDENTITY.with(|c| {
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
});
DASHED_IDENTITY
.write()
.unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, 线Py { inner }).unwrap();
DASHED_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
DASHED_IDENTITY
.write()
.unwrap()
.insert(key, obj.clone_ref(py));
obj
}
@@ -88,18 +101,23 @@ pub(crate) fn segfeat_to_py(
inner: Arc<chanlun::structure::segment_feat::线>,
) -> Py<线Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
SEGFEAT_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = SEGFEAT_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
SEGFEAT_IDENTITY.with(|c| {
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
});
SEGFEAT_IDENTITY
.write()
.unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, 线Py { inner }).unwrap();
SEGFEAT_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
SEGFEAT_IDENTITY
.write()
.unwrap()
.insert(key, obj.clone_ref(py));
obj
}
@@ -108,18 +126,23 @@ pub(crate) fn featfrac_to_py(
inner: Arc<chanlun::structure::feat_fractal::>,
) -> Py<Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) =
FEATFRAC_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
if let Some(cached) = FEATFRAC_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
FEATFRAC_IDENTITY.with(|c| {
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
});
FEATFRAC_IDENTITY
.write()
.unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, Py { inner }).unwrap();
FEATFRAC_IDENTITY.with(|c| {
c.write().unwrap().insert(key, obj.clone_ref(py));
});
FEATFRAC_IDENTITY
.write()
.unwrap()
.insert(key, obj.clone_ref(py));
obj
}
use crate::types_py::{Py, Py, Py};
@@ -469,15 +492,13 @@ impl 虚线Py {
}
#[getter]
fn (&self) -> Option<Self> {
fn (&self, py: Python<'_>) -> Option<Py<线Py>> {
self.inner
.
.read()
.unwrap()
.as_ref()
.map(|d| Self {
inner: Arc::clone(d),
})
.map(|d| dashed_to_py(py, Arc::clone(d)))
}
// ---- 序列 getters ----
@@ -486,12 +507,7 @@ impl 虚线Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in self.inner..read().unwrap().iter() {
list.append(Py::new(
py,
Self {
inner: Arc::clone(d),
},
)?)?;
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
}
@@ -530,12 +546,7 @@ impl 虚线Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in self.inner..read().unwrap().iter() {
list.append(Py::new(
py,
Self {
inner: Arc::clone(d),
},
)?)?;
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
}
@@ -582,11 +593,10 @@ impl 虚线Py {
let obs_ref = .borrow();
let observer_inner = obs_ref.obs();
let result = self.inner.K序列(&observer_inner.K线序列);
let list = pyo3::types::PyList::empty(.py());
let py = .py();
let list = pyo3::types::PyList::empty(py);
for k in &result {
list.append(K线Py {
inner: Arc::clone(k),
})?;
list.append(bar_to_py(py, Arc::clone(k)))?;
}
Ok(list.into())
}
@@ -973,7 +983,7 @@ impl 虚线Py {
) -> (bool, String) {
let obs = .borrow();
let obs_ref = obs.obs();
chanlun::structure::dash_line::线::(&线.borrow().inner, &*obs_ref)
chanlun::structure::dash_line::线::(&线.borrow().inner, &obs_ref)
}
}
@@ -1148,7 +1158,7 @@ impl 线段特征Py {
let inner = Arc::make_mut(&mut self.inner);
inner
.(Arc::clone(&线.borrow().inner))
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
.map_err(pyo3::exceptions::PyValueError::new_err)
}
/// :param 待删除虚线: 待删除的虚线
@@ -1156,7 +1166,7 @@ impl 线段特征Py {
let inner = Arc::make_mut(&mut self.inner);
inner
.(&Arc::clone(&线.borrow().inner))
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
.map_err(pyo3::exceptions::PyValueError::new_err)
}
// ---- classmethods ----
+6 -6
View File
@@ -545,7 +545,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
// 买卖点类型 class attributes (singleton instances)
let py = m.py();
let bsp_class = m.getattr("买卖点类型")?;
let bsp_class = bsp_class.downcast_into::<PyType>()?;
let bsp_class = bsp_class.cast_into::<PyType>()?;
let variants: &[(&str, chanlun::types::)] = &[
("一买", chanlun::types::::),
@@ -568,7 +568,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("T3B卖", chanlun::types::::T3B卖),
];
let mut bsp_members = PyDict::new(py);
let bsp_members = PyDict::new(py);
for (name, value) in variants {
let instance = Py::new(py, Py { inner: *value })?;
bsp_class.setattr(*name, instance.clone_ref(py))?;
@@ -577,7 +577,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
bsp_class.setattr("__members__", bsp_members)?;
// 相对方向 class attributes
let dir_class = m.getattr("相对方向")?.downcast_into::<PyType>()?.clone();
let dir_class = m.getattr("相对方向")?.cast_into::<PyType>()?.clone();
let dir_variants: &[(&str, chanlun::types::)] = &[
("向上", chanlun::types::::),
("向下", chanlun::types::::),
@@ -590,7 +590,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("", chanlun::types::::),
];
let mut dir_members = PyDict::new(py);
let dir_members = PyDict::new(py);
for (name, value) in dir_variants {
let instance = Py::new(py, Py { inner: *value })?;
dir_class.setattr(*name, instance.clone_ref(py))?;
@@ -599,7 +599,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
dir_class.setattr("__members__", dir_members)?;
// 分型结构 class attributes
let frac_class = m.getattr("分型结构")?.downcast_into::<PyType>()?.clone();
let frac_class = m.getattr("分型结构")?.cast_into::<PyType>()?.clone();
let frac_variants: &[(&str, chanlun::types::)] = &[
("", chanlun::types::::),
("", chanlun::types::::),
@@ -608,7 +608,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("", chanlun::types::::),
];
let mut frac_members = PyDict::new(py);
let frac_members = PyDict::new(py);
for (name, value) in frac_variants {
let instance = Py::new(py, Py { inner: *value })?;
frac_class.setattr(*name, instance.clone_ref(py))?;