标准库中的 thread.event 用于创建线程事件对象,
该对象类似于多线程中的交通信号灯、实现红绿灯机制实现多线程之间的同步协作。
创建 thread.event 对象的代码如下:
- import thread.event;
- var event = thread.event("交通信号灯")
复制代码
其中第一个参数指定一个唯一的名称、一般建议用aardio工具里的GUID工具生成一个GUID以保证其唯一性。
不同线程、或不同进程中使用相同的名称就可以打开同一个事件对象( thread.event对象不但可以跨线程、也可以跨进程使用 )使用 event.conflict 属性可以判断其他线程或进程是否打开了该事件对象。
thread.event有三个构造参数,如下:var event = thread.event("事件唯一名称",是否手动复原信号,初始状态) 参数2、参数3都是可选参数,默认都是false. 初始状态为false表示初始化为无信号状态, 如果手动复原信号为false表示自动复原信号,也就是每次放行线程以后自动重置为无信号状成。
如果把event比作交通信号灯,那么无信号状态类似红灯表示禁止通行,而有信号状态类似绿灯表示放行等待的线程,几个关键的函数: - import thread.event;
- var event = thread.event("交通信号灯")
- event.reset(); //重置为无线号状态、类似于切换为红灯
- event.set();//切换为有线号状态、类似于切换为绿灯
- event.wait(); //阻塞线程、并等待信号变为绿灯
- //如果不是手动复原的事件对象、执行到这里会自动复原为无信号状态(绿灯)
复制代码
- io.open()
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("交通信号灯")
-
- event.wait();
- io.print("汽车1 过了")
- }
- )
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("交通信号灯")
-
- event.wait();
- io.print("汽车2 过了")
- }
- )
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("交通信号灯")
-
- event.wait();
- io.print("汽车3 过了")
- }
- )
- //给一点时间让所有线程进入等待状态
- sleep(100)
- import thread.event;
- var event = thread.event("交通信号灯")
- event.set(); //切换为绿灯
复制代码 执行上面的代码以后,你会发现只有一部汽车过了(线程是并发执行的,所以可能是任意一部汽车先过),这是为什么呢?
thread.event的二个构造参数如果为false(默认就是这个值)那么创建的事件对象是自动复原信号的,当一个等待的线程被放行以后,该对象会立即自动切换为无信号状态( 用上面的示例来讲:就是绿灯放行一部车子就立即变为绿灯,一次只能过一部车子)
如果要一次放行所有等待的线程,那就要做如下修改,使事件对象使用手动复原机制,示例如下:- import thread.event;
- var event = thread.event("交通信号灯22",true/*手动复原*/)
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("交通信号灯22")
-
- event.wait();
- io.print("汽车1 过了")
- }
- )
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("交通信号灯22")
-
- event.wait();
- io.print("汽车2 过了")
- }
- )
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("交通信号灯22")
-
- event.wait();
- io.print("汽车3 过了")
- }
- )
- //给一点时间让所有线程进入等待状态
- sleep(100)
- io.open()
- io.print("绿灯了,给大家一秒钟的时间,要过的快过去")
- event.set(); //切换为绿灯
- sleep(1000)
- event.reset(); //切换为红灯
- io.print("红灯了,请遵守交通规则,耐心等待")
复制代码
下面再看一个例子:
- io.open()
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("对讲机")
-
- io.print("张三: 李四、收到请回复")
- event.set();//over
-
- event.wait();
- io.print("张三: 年夜饭吃过了吗")
- event.set();//over
-
- event.wait();
- io.print("张三: %¥#@!~")
- event.set();//over,让对方有机会说话
- }
- )
- thread.create(
- function(){
- import thread.event;
- var event = thread.event("对讲机")
-
- event.wait();
- io.print("李四: Hi、兄弟你好!")
- event.set();//over
-
- event.wait();
- io.print("李四: 还没吃呢,正在写个自动吃年夜饭的小程序")
- event.set();//over
-
- }
- )
复制代码 在实际的应用中,通常是一个线程event.wait() 等待另外一个线程执行 event.set(); 这样代码会更简单一些,如果需要实现更复杂的交互控制则可以创建多个thread.event对象。
注意 event.wait() 与 event.waitOne() 作用类似,区别是 event.waitOne() 用在界面线程中在等待的过程中可以处理用户的交互操作( 不会是无响应状态 ) 实际上这两个函数调用的是 thread.wait() 以及 thread.waitOne() 函数,用法也类似,可以指定一个超时时间。在超时以后返回null,利用这个特性可用来做定时器之类的线程内循环操作,下面是aardio范例中的一段代码:- //定时执行线程任务
- import win.ui;
- /*DSG{{*/
- var winform = ..win.form(text="使用thread.event创建定时执行任务的线程";right=349;bottom=211;parent=...)
- winform.add(
- btnStart={cls="button";text="启动定时线程";left=61;top=113;right=181;bottom=155;z=1};
- btnStop={cls="button";text="结束定时线程";left=192;top=113;right=312;bottom=155;disabled=1;z=4};
- lbTip={cls="static";left=26;top=85;right=100;bottom=103;transparent=1;z=3};
- static={cls="static";left=138;top=13;right=309;bottom=44;align="center";center=1;edge=1;z=5};
- trackbar={cls="trackbar";left=15;top=52;right=319;bottom=82;max=1000;min=500;z=2}
- )
- /*}}*/
- task_t = function(hwnd,ms){
- import win;
- import thread.event;
-
- var evt = thread.event("定时事件 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44")
- while( ! evt.wait(ms) ){
- win.setText(hwnd,tostring( time() ) )
- }
- io.print("任务已完成")
- }
- import thread.event;
- var evtTask = thread.event("定时事件 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44",false)
- winform.btnStart.oncommand = function(id,event){
- winform.btnStart.disabled = true;
- winform.btnStop.disabled = false;
- winform.trackbar.disabled = true;
-
- hThread = thread.create(task_t,winform.static.hwnd,winform.trackbar.pos);
- thread.waitOne(hThread)
-
- winform.btnStart.disabled = false;
- winform.btnStop.disabled = true;
- winform.trackbar.disabled = false;
- }
- winform.btnStop.oncommand = function(id,event){
- evtTask.set();//使事件对象切换为有信号状态,使wait函数退出
- }
- winform.trackbar.oncommand = function(id,event,pos){
- winform.lbTip.text = owner.pos + "毫秒"
- }
-
- winform.show()
- win.loopMessage();
复制代码
注意:使用 thread.create() 创建线程会返回线程句柄,而线程句柄需要使用 raw.closehandle() 关闭 - 该操作并不会结束线程。上面的代码省略了该步骤没有关闭线程句柄、实际运用中要注意及时关闭线程句柄。
或许您可能有这样的疑问,为什么不使用更简单的方法,例如循环读取一个标识变量来实现同样的功能呢?
要知道 thread.wait() event.wait() 函数能让线程停止下来 - 显然比不断的循环查询变量更高效。 |