搜索
查看: 12490|回复: 11

[入门教程] 多线程中的交通信号灯:thread.event

  [复制链接]

43

主题

679

帖子

3805

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3805
发表于 2014-1-30 18:10:14 | 显示全部楼层 |阅读模式
标准库中的 thread.event 用于创建线程事件对象,
该对象类似于多线程中的交通信号灯、实现红绿灯机制实现多线程之间的同步协作。

创建 thread.event 对象的代码如下:
  1. import thread.event;
  2. var event = thread.event("交通信号灯")
复制代码

其中第一个参数指定一个唯一的名称、一般建议用aardio工具里的GUID工具生成一个GUID以保证其唯一性。
不同线程、或不同进程中使用相同的名称就可以打开同一个事件对象( thread.event对象不但可以跨线程、也可以跨进程使用 )使用  event.conflict 属性可以判断其他线程或进程是否打开了该事件对象。

thread.event有三个构造参数,如下:
var event =  thread.event("事件唯一名称",是否手动复原信号,初始状态)
参数2、参数3都是可选参数,默认都是false.
初始状态为false表示初始化为无信号状态, 如果手动复原信号为false表示自动复原信号,也就是每次放行线程以后自动重置为无信号状成。

如果把event比作交通信号灯,那么无信号状态类似红灯表示禁止通行,而有信号状态类似绿灯表示放行等待的线程,几个关键的函数:
  1. import thread.event;
  2. var event = thread.event("交通信号灯")

  3. event.reset(); //重置为无线号状态、类似于切换为红灯
  4. event.set();//切换为有线号状态、类似于切换为绿灯
  5. event.wait(); //阻塞线程、并等待信号变为绿灯
  6. //如果不是手动复原的事件对象、执行到这里会自动复原为无信号状态(绿灯)
复制代码

  1. io.open()

  2. thread.create(
  3.     function(){
  4.         import thread.event;
  5.         var event = thread.event("交通信号灯")
  6.         
  7.         event.wait();
  8.         io.print("汽车1 过了")
  9.     }
  10. )

  11. thread.create(
  12.     function(){
  13.         import thread.event;
  14.         var event = thread.event("交通信号灯")
  15.         
  16.         event.wait();
  17.         io.print("汽车2 过了")
  18.     }
  19. )

  20. thread.create(
  21.     function(){
  22.         import thread.event;
  23.         var event = thread.event("交通信号灯")
  24.         
  25.         event.wait();
  26.         io.print("汽车3 过了")
  27.     }
  28. )

  29. //给一点时间让所有线程进入等待状态
  30. sleep(100)

  31. import thread.event;
  32. var event = thread.event("交通信号灯")
  33. event.set(); //切换为绿灯
复制代码
执行上面的代码以后,你会发现只有一部汽车过了(线程是并发执行的,所以可能是任意一部汽车先过),这是为什么呢?
thread.event的二个构造参数如果为false(默认就是这个值)那么创建的事件对象是自动复原信号的,当一个等待的线程被放行以后,该对象会立即自动切换为无信号状态( 用上面的示例来讲:就是绿灯放行一部车子就立即变为绿灯,一次只能过一部车子)

如果要一次放行所有等待的线程,那就要做如下修改,使事件对象使用手动复原机制,示例如下:
  1. import thread.event;
  2. var event = thread.event("交通信号灯22",true/*手动复原*/)

  3. thread.create(
  4.     function(){
  5.         import thread.event;
  6.         var event = thread.event("交通信号灯22")
  7.         
  8.         event.wait();
  9.         io.print("汽车1 过了")
  10.     }
  11. )

  12. thread.create(
  13.     function(){
  14.         import thread.event;
  15.         var event = thread.event("交通信号灯22")
  16.         
  17.         event.wait();
  18.         io.print("汽车2 过了")
  19.     }
  20. )

  21. thread.create(
  22.     function(){
  23.         import thread.event;
  24.         var event = thread.event("交通信号灯22")
  25.         
  26.         event.wait();
  27.         io.print("汽车3 过了")
  28.     }
  29. )

  30. //给一点时间让所有线程进入等待状态
  31. sleep(100)

  32. io.open()
  33. io.print("绿灯了,给大家一秒钟的时间,要过的快过去")
  34. event.set(); //切换为绿灯

  35. sleep(1000)
  36. event.reset(); //切换为红灯
  37. io.print("红灯了,请遵守交通规则,耐心等待")
复制代码

下面再看一个例子:
  1. io.open()

  2. thread.create(
  3.     function(){
  4.         import thread.event;
  5.         var event = thread.event("对讲机")
  6.         
  7.         io.print("张三: 李四、收到请回复")
  8.         event.set();//over
  9.          
  10.         event.wait();
  11.         io.print("张三: 年夜饭吃过了吗")
  12.         event.set();//over
  13.          
  14.         event.wait();
  15.         io.print("张三: %¥#@!~")
  16.         event.set();//over,让对方有机会说话
  17.     }
  18. )       



  19. thread.create(
  20.     function(){
  21.         import thread.event;
  22.         var event = thread.event("对讲机")
  23.      
  24.         event.wait();
  25.         io.print("李四: Hi、兄弟你好!")  
  26.         event.set();//over
  27.          
  28.         event.wait();
  29.         io.print("李四: 还没吃呢,正在写个自动吃年夜饭的小程序")
  30.         event.set();//over
  31.         
  32.     }
  33. )
复制代码
在实际的应用中,通常是一个线程event.wait() 等待另外一个线程执行 event.set(); 这样代码会更简单一些,如果需要实现更复杂的交互控制则可以创建多个thread.event对象。


注意 event.wait() 与 event.waitOne() 作用类似,区别是 event.waitOne() 用在界面线程中在等待的过程中可以处理用户的交互操作( 不会是无响应状态 ) 实际上这两个函数调用的是  thread.wait() 以及  thread.waitOne() 函数,用法也类似,可以指定一个超时时间。在超时以后返回null,利用这个特性可用来做定时器之类的线程内循环操作,下面是aardio范例中的一段代码:
  1. //定时执行线程任务
  2. import win.ui;
  3. /*DSG{{*/
  4. var winform = ..win.form(text="使用thread.event创建定时执行任务的线程";right=349;bottom=211;parent=...)
  5. winform.add(
  6. btnStart={cls="button";text="启动定时线程";left=61;top=113;right=181;bottom=155;z=1};
  7. btnStop={cls="button";text="结束定时线程";left=192;top=113;right=312;bottom=155;disabled=1;z=4};
  8. lbTip={cls="static";left=26;top=85;right=100;bottom=103;transparent=1;z=3};
  9. static={cls="static";left=138;top=13;right=309;bottom=44;align="center";center=1;edge=1;z=5};
  10. trackbar={cls="trackbar";left=15;top=52;right=319;bottom=82;max=1000;min=500;z=2}
  11. )
  12. /*}}*/

  13. task_t = function(hwnd,ms){
  14.         import win;
  15.         import thread.event;
  16.        
  17.         var evt = thread.event("定时事件 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44")
  18.         while( ! evt.wait(ms) ){
  19.                 win.setText(hwnd,tostring( time() ) )
  20.         }
  21.         io.print("任务已完成")
  22. }

  23. import thread.event;
  24. var evtTask = thread.event("定时事件 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44",false)

  25. winform.btnStart.oncommand = function(id,event){

  26.         winform.btnStart.disabled = true;
  27.         winform.btnStop.disabled = false;
  28.         winform.trackbar.disabled = true;
  29.        
  30.         hThread = thread.create(task_t,winform.static.hwnd,winform.trackbar.pos);
  31.         thread.waitOne(hThread)
  32.        
  33.         winform.btnStart.disabled = false;
  34.         winform.btnStop.disabled = true;
  35.         winform.trackbar.disabled = false;
  36. }

  37. winform.btnStop.oncommand = function(id,event){
  38.         evtTask.set();//使事件对象切换为有信号状态,使wait函数退出
  39. }

  40. winform.trackbar.oncommand = function(id,event,pos){
  41.         winform.lbTip.text = owner.pos + "毫秒"
  42. }

  43. winform.show()
  44. win.loopMessage();
复制代码


注意:使用 thread.create() 创建线程会返回线程句柄,而线程句柄需要使用  raw.closehandle() 关闭 - 该操作并不会结束线程。上面的代码省略了该步骤没有关闭线程句柄、实际运用中要注意及时关闭线程句柄。

或许您可能有这样的疑问,为什么不使用更简单的方法,例如循环读取一个标识变量来实现同样的功能呢?
要知道 thread.wait() event.wait() 函数能让线程停止下来 - 显然比不断的循环查询变量更高效。

评分

参与人数 1银币 +10 收起 理由
清顽于世 + 10 第二段源代码里, "...这里会自动复原为无.

查看全部评分

回复

使用道具 举报

37

主题

184

帖子

1240

积分

四级会员

Rank: 6Rank: 6

积分
1240
发表于 2014-1-30 21:15:45 | 显示全部楼层

新年快乐,路过留名

新年快乐,路过留名
回复

使用道具 举报

4

主题

24

帖子

165

积分

培训班

积分
165
发表于 2014-1-30 21:52:52 | 显示全部楼层

三十晚上还写教程!!!谢谢, 真的辛苦了!!!

三十晚上还写教程!!!谢谢, 真的辛苦了!!!
回复

使用道具 举报

0

主题

2

帖子

27

积分

新手入门

Rank: 1

积分
27
发表于 2014-1-30 22:03:44 | 显示全部楼层

新年快乐!辛苦了!

新年快乐!辛苦了!
回复

使用道具 举报

16

主题

138

帖子

932

积分

三级会员

Rank: 4

积分
932
发表于 2014-1-31 16:28:13 | 显示全部楼层

感谢编程,祝所有aardio爱好者新年快乐!

感谢编程,祝所有aardio爱好者新年快乐!
原创网http://fanal.cn谢谢支持
回复

使用道具 举报

3

主题

26

帖子

234

积分

荣誉会员

Rank: 8Rank: 8

积分
234
发表于 2014-2-2 23:00:55 | 显示全部楼层

认真学习!谢谢奉献教程!

认真学习!谢谢奉献教程!
回复

使用道具 举报

17

主题

97

帖子

4万

积分

新手入门

积分
44038
发表于 2014-2-7 16:29:24 | 显示全部楼层

顶,这个要看看

顶,这个要看看
有太多东西要学,可是感觉没时间。
回复

使用道具 举报

3

主题

12

帖子

90

积分

一级会员

Rank: 2

积分
90
发表于 2014-8-7 14:24:25 | 显示全部楼层

谢谢,正好在摸索。觉得aau的好人多多啊。

谢谢,正好在摸索。觉得aardio的好人多多啊。
回复

使用道具 举报

0

主题

10

帖子

70

积分

一级会员

Rank: 2

积分
70
发表于 2014-9-4 22:17:29 | 显示全部楼层

方便学习,收藏了。

方便学习,收藏了。
回复

使用道具 举报

1

主题

5

帖子

48

积分

新手入门

Rank: 1

积分
48
QQ
发表于 2015-4-17 14:00:52 | 显示全部楼层

很不错的教程,如此详细的讲解!!

很不错的教程,如此详细的讲解!!
回复

使用道具 举报

1

主题

54

帖子

371

积分

培训班

积分
371
发表于 2016-4-24 15:50:52 | 显示全部楼层

信息量有点大,慢慢学习

信息量有点大,慢慢学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

手机版|未经许可严禁引用或转载本站文章|站长邮箱|aardio.com|aardio官方社区 ( 皖ICP备09012014号 )

GMT+8, 2018-10-24 11:45 , Processed in 0.062500 second(s), 24 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表