YStudio Y++

多线程支持库

Y++ / YStudio 开发文档 · 一点滴

扁平命令式支持库(不是类)。路径:

  • 支持库 → 多线程支持库 → 多线程操作启动线程 等)
  • 支持库 → 多线程支持库 → 线程池操作创建线程池 / 投递任务 等)

任务回调参数支持 无参、标量(非数组)、数组,以及混用——不必是数组

硬性约定:多线程不得卡住 UI。 不要在按钮/单击等界面事件里调用 等待线程 / 等待所有线程 / 等待线程池 / 等待任务。界面收尾用 置线程池完成事件 / 置线程池空闲事件;若必须同步等待,请放进 启动线程(&工作) 里。

快速开始:不卡界面(最推荐)

整数 池

函数 只算标量(整数 n) {
    // 重活在工作线程
}

函数 全部完成() {
    标签1.标题 = "全部完成"
    关闭线程池(池)
}

函数 按钮1.单击(整数 x, 整数 y) {
    池 = 创建线程池(0)
    置线程池空闲事件(池, &全部完成)
    整数 i
    循环(100, i) {
        投递任务(池, &只算标量, i)   // 非数组
    }
    // 立刻返回 —— 绝不在这里 等待线程池
}

快速开始:后台同步等待(亦可)

函数 写回数组(整数 n, 整数 结果数组[]) {
    结果数组[n + 1] = n
}

函数 批量工作() {
    整数 池 = 创建线程池(0)   // 0=按 CPU 核数
    整数 结果数组[1000]
    整数 i
    循环(1000, i) {
        投递任务(池, &写回数组, i, 结果数组)
    }
    等待线程池(池)   // 在后台线程等待,不卡界面
    关闭线程池(池)
}

函数 按钮1.单击(整数 x, 整数 y) {
    启动线程(&批量工作)   // 立即返回,界面不卡
}

回调参数:数组与非数组

无参

函数 干活() {
    输出("ok")
}
投递任务(池, &干活)

仅非数组(标量)

函数 只算标量(整数 n) {
    输出(n)
}
投递任务(池, &只算标量, i)

仅数组

函数 填结果(整数 结果数组[]) {
    结果数组[1] = 1
}
投递任务(池, &填结果, 结果数组)

标量 + 数组混用

函数 混用(整数 n, 文本 前缀, 整数 结果数组[]) {
    结果数组[n + 1] = 到整数(前缀) + n
}
投递任务(池, &混用, i, "10", 结果数组)

说明:

  • 标量(整数/文本/逻辑等):投递瞬间按值拷贝,各任务互不影响
  • 局部数组:按引用写回;多任务写同一下标请自行分区(例如每任务写 结果[i+1]
  • 启动线程(&函数, …) 使用同一套规则

何时用线程 / 何时用线程池

场景 建议
单个后台任务、不卡界面 启动线程(&工作)
少量并行(几个) 启动线程 + 等待所有线程
成百上千同类任务 线程池:固定工作线程,任务排队

多线程操作(命令一览)

命令 返回 说明
启动线程(&函数[, 参数...]) 整数句柄 立即返回;参数可为标量/数组/混用
等待线程(句柄) 逻辑 等一个线程结束
等待所有线程(句柄数组) (空) 等一批线程;UI 等待会泵消息
暂停线程 / 继续线程 逻辑 勿用于主/UI/当前线程
强制结束线程 逻辑 仅卡死时用

线程池操作(命令一览)

命令 返回 说明
创建线程池([线程数]) 整数池句柄 省略或 0=CPU 核数(至少 1)
投递任务(池, &函数[, 参数...]) 整数任务序号 入队即返回;支持无参/标量/数组/混用
等待线程池(池) 逻辑 排队与执行中全部完成
关闭线程池(池) 逻辑 排空后结束工作线程;关闭前建议先等待
取线程池排队数(池) 整数 尚未取走的任务数;无效句柄 -1
取线程池工作中数(池) 整数 正在执行的任务数;无效句柄 -1
取线程池线程数(池) 整数 工作线程数(创建时确定);无效句柄 -1
置线程池完成事件(池, &回调) 逻辑 每任务完成后在界面线程回调;形参 函数 名(整数 任务序号);传 0 清除
置线程池空闲事件(池, &回调) 逻辑 池变空闲时在界面线程回调;无参 函数 名();传 0 清除
等待任务(池, 任务序号) 逻辑 等单个任务;勿在界面事件里调用(可放 启动线程 内)
取消排队任务(池, 任务序号) 逻辑 仅取消尚未开始的排队任务;已开始返回假;可在界面调用

池句柄与线程句柄不可混用(不能对池调用 暂停线程 等)。

等待任务 / 取消排队(后台用)

函数 后台等一个() {
    整数 池 = 创建线程池(0)
    整数 id = 投递任务(池, &只算标量, 1)
    // 若还在排队可取消:取消排队任务(池, id)
    等待任务(池, id)
    关闭线程池(池)
}
函数 按钮1.单击(整数 x, 整数 y) {
    启动线程(&后台等一个)   // 界面不卡
}

界面完成回调(推荐不卡界面收尾)

回调经主窗口消息投递,在界面线程执行,可直接改控件。任务函数本身仍支持无参 / 标量 / 数组 / 混用。

整数 池

函数 只算标量(整数 n) {
    // 重活(工作线程)
}

函数 任务完成(整数 任务号) {
    标签1.标题 = "完成 " + 到文本(任务号)
}

函数 全部完成() {
    标签1.标题 = "全部完成"
    关闭线程池(池)
}

函数 按钮1.单击(整数 x, 整数 y) {
    池 = 创建线程池(0)
    置线程池完成事件(池, &任务完成)
    置线程池空闲事件(池, &全部完成)
    整数 i
    循环(100, i) {
        投递任务(池, &只算标量, i)   // 非数组
    }
    // 不必等待:空闲事件会通知界面
}

进度查询示例

可在工作函数里轮询,或配合定时器刷新界面(查询很快):

函数 只算标量(整数 n) {
    // 重活…
}

函数 批量并报进度() {
    整数 池 = 创建线程池(0)
    整数 i
    循环(500, i) {
        投递任务(池, &只算标量, i)   // 非数组回调
    }
    输出("工作线程=" + 到文本(取线程池线程数(池)))
    循环(真) {
        整数 排队 = 取线程池排队数(池)
        整数 执行中 = 取线程池工作中数(池)
        输出("排队=" + 到文本(排队) + " 执行中=" + 到文本(执行中))
        如果 (排队 <= 0 且 执行中 <= 0) {
            跳出()
        }
        延时(50)
    }
    关闭线程池(池)
}

注意

  • 退出循环请用流程命令 跳出()(不是「跳出循环」);继续() 跳到下一轮。二者只能写在 循环 / / 遍历{} 内部
  • 禁止在按钮/单击等界面事件里同步 等待所有线程 / 等待线程池 / 等待任务——会卡住界面逻辑;请用完成/空闲事件,或 启动线程(&工作) 后再等待
  • 禁止在池任务回调(工作线程)里对同一池再调 等待线程池(会死锁);界面完成/空闲回调里也不要同步等待同一池
  • 取消排队任务 不能打断已开始执行的任务
  • 主窗口关闭时会强制回收未结束的工作线程与线程池