aardio中的多线程

前言

官方文档

https://bbs.aardio.com/forum.php?mod=viewthread&tid=13625

虽然 aardio 的多线程开发非常简单,但是:
1、请先了解:「多线程」开发比「单线程」开发更复杂这个残酷的现实。
2、请先了解: aardio 这样的动态语言可以实现真多线程非常罕见。

什么是线程

当你点击EXE文件系统一个应用程序的时候 - 系统会创建一个进程(process),而在一个进程内可以包含多个线程(thread)。

用来显示界面的线程,我们通常称为“界面线程”,其他不是用来显示界面的线程,我们一般称为“工作线程”或者是“后台线程”。
进程的启动线程称为「主线程」,「界面线程」通常是主线程。

多线程开发基本规则

多线程开发时要谨记以下基本规则。

1、非主线程的错误信息默认只会输出到控制台
只有用 console.open() 或 io.open() 打开控制台才能看到非主线程的错误信息。

2、每个线程有独立的运行上下文、独立的全局变量环境,有独立的堆栈。
一个线程不会使用另一个线程的全局部变量。
一个线程也不会使用另一个线程引入的库。

3、不是所有对象都可以从一个线程传到另一个线程使用

可以传递的类型:

  • 没有任何外部依赖的数值、字符串、buffer、table、function 可以传入其他线程使用。
    这些对象在传入另一个线程时通常会复制值 - 也就是传值而非传址。

  • 类不可以从一个线程传入另一个线程使用。

  • 类创建的实例对象,除非文档有特别说明一般不可以传入另一个线程使用。

  • win.form 创建的窗体对象以及该窗体上创建的控件对象都可以作为参数传入其他线程。
    在其他线程调用窗体与控件对象的成员函数时 —— 都会回发到创建窗体的界面线程执行。
    利用这种奇妙的特性 —— 实际上可以在工作线程调用界面线程的任意代码。

  • COM 对象不可以从一个线程传递到另一个线程。

  • 以下对象可从一个线程传递到另一个线程:

    1
    2
    3
    time,time.ole,thread.var,thread.table,
    thread.command,thread.event,thread.semaphore,process.mutex,
    fsys.file,fsys.stream,fsys.mmap,raw.struct

简单的示例

1
2
3
4
5
6
thread.invoke( 
function(){
import console;
console.log("线程在执行",thread.getId())
}
)

web.form不支持多线程

先看一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import win.ui;
/*DSG{{*/
var winform = win.form(text="线程测试";right=1123;bottom=570)
/*}}*/

import web.form;
var mb = web.form.ie11(winform);

//显示窗口
winform.show();

//使用浏览器打开网页
mb.go("https://www.psvmc.cn")
mb.wait();

var titleTags = mb.document.getElementsByTagName("title")
for i, title in com.each(titleTags) {
if (i == 1) {
winform.text = title.innerHTML;
break;
}
}
//启动界面线程消息循环
win.loopMessage();

因为我们要取页面的标题,所以我们要等待页面加载完毕mb.wait();,这样就阻塞了主线程,这时候页面中的所有按钮都无法响应了,所以给人卡顿的感觉。

要想不阻塞主线程就要用多线程,但是var mb = web.form.ie11(winform);,其中的mb是不支持线程间传递的,所以没有什么更好的方法。

我们可以把

1
mb.wait();

修改为

1
mb.waitDoc();

来稍微优化一下体验。

invokeAndWait

我们有时候在界面中创建一个线程,仅仅是为了让界面不卡顿,我们希望用 thead.waitOne() 阻塞等待线程执行完闭(界面线程同时可以响应消息),然后我们又希望在后面关闭线程句柄,并获取到线程最后返回的值。

可能我们希望一切尽可能的简单,尽可能的少写代码,并且也不想用到thread.manage(因为并不需要管理多个线程)。

这时候我们可以使用 thread.invokeAndWaitthread.invokeAndWait 的参数和用法与 thread.invoke 完全一样,区别是 thread.invokeAndWait 会阻塞并等待线程执行完毕,并关闭线程句柄,同时获取到线程函数的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=759;bottom=469)
winform.add(
button={cls="button";text="读取网页";left=190;top=351;right=542;bottom=423;dl=1;dr=1;z=1};
edit={cls="edit";text="edit";left=48;top=40;right=720;bottom=336;edge=1;multiline=1;z=2}
)
/*}}*/

winform.button.oncommand = function(id,event){
winform.button.disabledText = {"✶";"✸";"✹";"✺";"✹";"✷"}

winform.edit.text = thread.invokeAndWait(
function(winform){
//暂停模拟一个耗时的操作
sleep(3000);
import inet.http;
return inet.http().get("https://www.psvmc.cn/login.json");
},winform
)

winform.button.disabledText = null;
}

winform.show()
win.loopMessage();