跳到主要内容
版本:2.0.0

Threading - 线程和定时器

EasyBot 提供了完整的定时器功能和线程同步机制,支持延时执行、间隔执行以及多线程环境下的互斥锁操作。

threading 命名空间

threading 命名空间包含定时器和互斥锁相关的函数。

setTimeout()

创建一个定时器,在指定的延迟后执行回调函数。

语法: threading.setTimeout(callback: () => void, delay: number): number

参数:

  • callback - 要执行的回调函数
  • delay - 延迟时间(毫秒)

返回值: 定时器ID

示例:

const timerId = threading.setTimeout(() => {
logger.info("3秒后执行");
}, 3000);

setInterval()

创建一个间隔定时器,每隔指定的时间执行回调函数。

语法: threading.setInterval(callback: () => void, interval: number): number

参数:

  • callback - 要执行的回调函数
  • interval - 间隔时间(毫秒)

返回值: 定时器ID

示例:

const intervalId = threading.setInterval(() => {
logger.info("每5秒执行一次");
}, 5000);

clearTimeout()

清除定时器。

语法: threading.clearTimeout(timerId: number): boolean

参数:

  • timerId - 定时器ID

返回值: 是否成功清除

示例:

const timerId = threading.setTimeout(() => {
logger.info("这不会执行");
}, 1000);

const success = threading.clearTimeout(timerId);
logger.info(`清除成功: ${success}`);

clearInterval()

清除间隔定时器。

语法: threading.clearInterval(timerId: number): boolean

参数:

  • timerId - 定时器ID

返回值: 是否成功清除

示例:

const intervalId = threading.setInterval(() => {
logger.info("定期执行");
}, 1000);

// 10秒后停止
threading.setTimeout(() => {
const success = threading.clearInterval(intervalId);
logger.info(`停止间隔定时器: ${success}`);
}, 10000);

createMutex()

创建一个互斥锁 (Mutex),用于在多线程环境中同步访问共享资源。

语法 1: threading.createMutex(name: string): Mutex

语法 2: threading.createMutex(initiallyOwned: boolean, name: string): Mutex

参数:

  • initiallyOwned - (可选) 是否初始为已占用状态,默认为 false
  • name - 互斥锁名称,用于标识系统范围内的互斥锁

返回值: Mutex 对象

示例:

const mutex = threading.createMutex("my-global-lock");

Mutex 接口

Mutex 对象提供了线程同步的原语。

WaitOne()

阻塞当前线程,直到收到信号或超时。

语法 1: WaitOne(): boolean (无限期等待) 语法 2: WaitOne(millisecondsTimeout: number): boolean 语法 3: WaitOne(millisecondsTimeout: number, exitContext: boolean): boolean

参数:

  • millisecondsTimeout: 等待的毫秒数,-1 表示无限等待。
  • exitContext: 是否在等待前退出同步上下文(通常用不到,默认为 false)。

返回值:

  • true: 成功获取到锁。
  • false: 超时未获取到锁。

ReleaseMutex()

释放当前线程对互斥体的占用。

语法: ReleaseMutex(): void

注意: 只有拥有锁的线程才能释放锁,否则会抛出异常。

使用示例

const mutex = threading.createMutex("resource-lock");

function safeOperation() {
logger.info("尝试获取锁...");
// 尝试获取锁,最多等待 5 秒
if (mutex.WaitOne(5000)) {
try {
logger.info("成功获取锁,开始执行操作");
// 模拟耗时操作
// ...
} finally {
logger.info("释放锁");
mutex.ReleaseMutex();
}
} else {
logger.warning("获取锁超时");
}
}

全局定时器函数

EasyBot 还提供了兼容标准 JavaScript 的全局定时器函数。

setTimeout()

全局版本的 setTimeout 函数,支持额外参数传递。

语法: setTimeout(callback: Function, delay?: number, ...args: any[]): number

参数:

  • callback - 要执行的回调函数
  • delay - 延迟时间(毫秒),默认为0
  • args - 传递给回调函数的参数

返回值: 定时器ID

示例:

// 基础用法
const timerId = setTimeout(() => {
logger.info("延时执行");
}, 2000);

// 传递参数
setTimeout((name, age) => {
logger.info(`Hello ${name}, you are ${age} years old`);
}, 1000, "张三", 25);

setInterval()

全局版本的 setInterval 函数,支持额外参数传递。

语法: setInterval(callback: Function, interval?: number, ...args: any[]): number

参数:

  • callback - 要执行的回调函数
  • interval - 间隔时间(毫秒),默认为0
  • args - 传递给回调函数的参数

返回值: 定时器ID

示例:

// 基础用法
const intervalId = setInterval(() => {
logger.info("定期执行");
}, 3000);

// 传递参数
setInterval((message) => {
logger.info(message);
}, 2000, "定时消息");

clearTimeout()

全局版本的 clearTimeout 函数。

语法: clearTimeout(timerId: number): void

参数:

  • timerId - 定时器ID

示例:

const timerId = setTimeout(() => {
logger.info("这不会执行");
}, 5000);

clearTimeout(timerId);

clearInterval()

全局版本的 clearInterval 函数。

语法: clearInterval(timerId: number): void

参数:

  • timerId - 定时器ID

示例:

const intervalId = setInterval(() => {
logger.info("定期执行");
}, 1000);

// 5秒后停止
setTimeout(() => {
clearInterval(intervalId);
}, 5000);

实用示例

延时发送消息

bus.on("group_message_event", (event) => {
if (event.RawMessage === "/remind") {
event.Context.Reply(new MessageChain().Text("我会在30秒后提醒你"));

setTimeout(() => {
event.Context.Reply(new MessageChain().Text("⏰ 时间到了!"));
}, 30000);
}
});

定期任务

// 每小时发送一次群公告
const hourlyAnnouncement = setInterval(() => {
const message = new MessageChain()
.Text("📢 每小时公告:请遵守群规,文明聊天!");

// 向所有群发送消息(需要根据实际情况获取群列表)
// context.SendGroupMessageAsync(selfId, groupId, message);
}, 3600000); // 1小时 = 3600000毫秒

倒计时功能

function startCountdown(context, selfId, peerId, seconds) {
let remaining = seconds;

const countdownInterval = setInterval(() => {
if (remaining <= 0) {
clearInterval(countdownInterval);
context.SendGroupMessageAsync(
selfId,
peerId,
new MessageChain().Text("⏰ 倒计时结束!")
);
return;
}

if (remaining <= 10 || remaining % 10 === 0) {
context.SendGroupMessageAsync(
selfId,
peerId,
new MessageChain().Text(`倒计时: ${remaining}`)
);
}

remaining--;
}, 1000);

return countdownInterval;
}

// 使用示例
bus.on("group_message_event", (event) => {
if (event.RawMessage.startsWith("/countdown ")) {
const seconds = parseInt(event.RawMessage.split(" ")[1]);
if (seconds > 0 && seconds <= 300) { // 最多5分钟
startCountdown(event.AdapterContext, event.SelfId, event.PeerId, seconds);
}
}
});

定时清理任务

const activeUsers = new Map();

// 记录用户活动
bus.on("group_message_event", (event) => {
activeUsers.set(event.SenderId, Date.now());
});

// 每10分钟清理不活跃用户记录
setInterval(() => {
const now = Date.now();
const tenMinutes = 10 * 60 * 1000;

for (const [userId, lastActive] of activeUsers.entries()) {
if (now - lastActive > tenMinutes) {
activeUsers.delete(userId);
}
}

logger.info(`活跃用户数量: ${activeUsers.size}`);
}, 600000); // 10分钟

跨线程/进程文件写入保护 (Mutex)

当多个插件或线程可能同时写入同一个文件时,使用 Mutex 防止冲突。

const fs = require('fs');
const path = require('path');
const fileLock = threading.createMutex("my-data-file-lock");
const dataPath = path.join(__dirname, "data.json");

function writeDataSafe(data) {
if (fileLock.WaitOne(2000)) { // 等待最多2秒
try {
fs.writeFileSync(dataPath, JSON.stringify(data));
logger.info("数据写入成功");
} catch (err) {
logger.error("写入失败: " + err.message);
} finally {
fileLock.ReleaseMutex();
}
} else {
logger.error("无法获取文件锁,写入放弃");
}
}

注意事项

  1. Mutex 释放: 务必在 finally 块中调用 ReleaseMutex,确保锁一定会被释放。
  2. 死锁风险: 避免在持有锁时进行长时间操作或请求其他锁。
  3. 内存管理: 记得清理不再需要的定时器,避免内存泄漏。
  4. 错误处理: 在定时器回调中添加错误处理。
  5. 性能考虑: 避免创建过多的定时器。

最佳实践

  1. 使用 threading 命名空间: 推荐使用 threading.setTimeout 等函数,可以获得清除操作的返回值。
  2. 使用 Mutex 保护共享资源: 在并发环境下操作文件、数据库或其他共享资源时使用 Mutex。
  3. 保存定时器ID: 将定时器ID保存到变量中,以便后续清理。
  4. 异常处理: 在定时器回调中使用 try-catch。
  5. 资源清理: 在插件卸载时清理所有定时器。

遇到麻烦了?

我们提供有偿代安装服务,解决您的环境配置烦恼。

了解详情