预热线程增加 I/O 异常保护,避免进程关闭时 file closed 报错

This commit is contained in:
2569718930@qq.com
2026-05-25 06:18:55 +08:00
parent ccc21b7e9d
commit dc5bd9a496
3 changed files with 61 additions and 18 deletions
@@ -8,7 +8,6 @@ import {
ChevronLeft,
ChevronRight,
GraduationCap,
LineChart,
Menu,
Search,
Table2,
@@ -473,7 +472,6 @@ function PolyWeatherTerminal({
const NAV_ITEMS = [
{ key: "contracts", Icon: Table2, labelEn: "Contracts", labelZh: "天气合约" },
{ key: "signals", Icon: LineChart, labelEn: "Signals", labelZh: "交易信号" },
{ key: "markets", Icon: Activity, labelEn: "Markets", labelZh: "市场概览" },
{ key: "training", Icon: GraduationCap, labelEn: "Training", labelZh: "训练数据" },
];
@@ -364,6 +364,27 @@ function buildChartDomain(
return [Number((min - padding).toFixed(1)), Number((max + padding).toFixed(1))];
}
function buildProbRefLines(row: ScanOpportunityRow | null) {
const buckets = row?.distribution_full?.length
? row.distribution_full
: row?.distribution_preview;
if (!buckets?.length) return [];
const maxProb = Math.max(...buckets.map((b) => b.model_probability ?? 0), 0.01);
return buckets
.filter((b) => validNumber(b.value) !== null && (b.model_probability ?? 0) > 0)
.map((b) => {
const prob = b.model_probability ?? 0;
const intensity = prob / maxProb; // 0..1
return {
y: b.value!,
prob,
stroke: `rgba(37, 99, 235, ${(0.12 + intensity * 0.55).toFixed(2)})`,
width: 0.6 + intensity * 2.4,
label: `${(prob * 100).toFixed(0)}%`,
};
});
}
export function LiveTemperatureThresholdChart({
isEn,
row,
@@ -414,6 +435,7 @@ export function LiveTemperatureThresholdChart({
.slice(0, 5)
.map((item) => ({ ...item, ...seriesStats(item.values) }));
const marketTemperatureTicks = useMemo(() => buildMarketTemperatureOptions(row), [row]);
const probRefLines = useMemo(() => buildProbRefLines(row), [row]);
const chartDomain = useMemo(
() => buildChartDomain(marketTemperatureTicks, series),
[marketTemperatureTicks, series],
@@ -489,6 +511,19 @@ export function LiveTemperatureThresholdChart({
label={{ value: `UMA ${threshold.toFixed(1)}°`, fill: "#f97316", fontSize: 10, position: "left" }}
/>
)}
{probRefLines.map((item, i) => (
<ReferenceLine
key={`prob-${i}`}
y={item.y}
stroke={item.stroke}
strokeWidth={item.width}
label={
item.prob >= 0.15
? { value: item.label, fill: "#2563eb", fontSize: 9, position: "insideRight" }
: undefined
}
/>
))}
<Tooltip
contentStyle={{
border: "1px solid #cbd5e1",
+26 -16
View File
@@ -1581,21 +1581,31 @@ def start_scan_terminal_prewarm() -> None:
def _run():
started = time.time()
ok = 0
workers = max(1, min(SCAN_TERMINAL_MAX_WORKERS, len(city_names)))
with ThreadPoolExecutor(max_workers=workers) as ex:
futures = {ex.submit(_warm_one, c): c for c in city_names}
for f in as_completed(futures):
try:
f.result()
ok += 1
except Exception:
pass
elapsed = int(time.time() - started)
logger.info(
"scan terminal pre-warm finished ok={}/{} elapsed={}s",
ok,
len(city_names),
elapsed,
)
try:
workers = max(1, min(SCAN_TERMINAL_MAX_WORKERS, len(city_names)))
with ThreadPoolExecutor(max_workers=workers) as ex:
futures = {ex.submit(_warm_one, c): c for c in city_names}
for f in as_completed(futures):
try:
f.result()
ok += 1
except Exception:
pass
elapsed = int(time.time() - started)
logger.info(
"scan terminal pre-warm finished ok={}/{} elapsed={}s",
ok,
len(city_names),
elapsed,
)
except (ValueError, OSError, IOError):
# Process is shutting down — file handles / threads may be closed
logger.info(
"scan terminal pre-warm interrupted (shutdown) ok={}/{}",
ok,
len(city_names),
)
except Exception:
logger.exception("scan terminal pre-warm failed")
threading.Thread(target=_run, name="scan-prewarm", daemon=True).start()