vb.net如何用线程池实现多线程事件

程序描述如下:

主线程会持续发出一系列EventClock事件,所有的10000个CWacher对象都会对每一个EventClock事件做出相应。

在单线程情况下,所有10000个CWatcher的对象是按照addhandler..addressof..语句定义的顺序依次执行响应程序的。

问题是这样:我希望当第一个EventClock事件发出后,所有CWatcher通过线程池操作并发响应这个事件,而主线程等待所有对象响应完成之后再发出第二个EventClock事件。请问如何能够实现呢?这个问题已经难倒过很多同行高手了~
分享到QQ空间

用户名:boblaw  得分:100  时间:2007-11-27 11:35:06
VB.NET code
     
     
' 主線程中聲明一個計算器,和一個對象(用來同步) Dim counter As Integer = 0 Dim syncobj As Object ' 要觸發事件前,先做一個判斷,通過查看計算器,得出是否所有的CWatcher已經響應事件,如果全部響應,才觸發 If counter Mod 10000 = 0 Then ' 觸發事件 Else ' 不觸發 End If ' 在CWatcher響應事件的過程代碼中,加入下面的代碼 SyncLock syncobj counter += 1 End SyncLock

用户名:wolfxin2010  得分:0  时间:2011-07-16 16:08:52
up up up ,有所收获!

用户名:msdn165168  得分:0  时间:2011-04-03 19:03:40
绝对是高手,学习一下

用户名:wuyazhe  得分:0  时间:2010-12-23 10:36:36
又挖坟贴。。。
版主结贴之

用户名:pp420  得分:0  时间:2010-12-23 10:19:15
高手。。

用户名:sp1234  得分:0  时间:2010-05-20 09:10:00
“所有的10000个CWacher对象”?

感觉上,还是lz留着这个程序自己玩吧。没有设计,就不要深入了。

用户名:my_skysea  得分:0  时间:2010-05-20 09:03:28
顶,收藏了!NB!

用户名:decacrf  得分:0  时间:2010-04-26 15:51:33
mark一下

用户名:kuhura  得分:0  时间:2010-04-22 23:03:27
mark一下

用户名:nandi_1  得分:0  时间:2010-04-22 19:32:13
标记
学习



用户名:my13513480001  得分:0  时间:2010-04-22 11:15:07
【多线程】——占位学习!!!!

用户名:lsh2216024  得分:0  时间:2010-03-12 11:26:13
学习一下了。。。。。。

用户名:Going1981  得分:0  时间:2010-03-11 23:22:47
收藏了,看得一头雾水的,改天来认真学习!

用户名:wsgwywzh  得分:0  时间:2010-03-04 09:35:34
还不太明白,要加强学习.

用户名:sfp_801  得分:0  时间:2010-03-03 11:36:13
有点深,不太明白,来拿分的

用户名:rainbowsoftware  得分:0  时间:2008-07-01 14:48:50
mark

用户名:limx001  得分:0  时间:2008-04-05 12:31:27
终于遇到高人了!!! 太佩服了,虽然看不懂,但还是要顶一下,实在太厉害了!

用户名:_NET2004  得分:0  时间:2008-04-05 11:44:28
绝对是高手,学习一下

用户名:xiao1zhao  得分:0  时间:2008-04-05 10:49:40
遇到高人了,佩服下~~

用户名:kockoc4  得分:0  时间:2008-03-31 17:51:42
編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com編程王10TB代碼庫
http://code.kingofcoders.com

10TB代碼Search engine
http://search.kingofcoders.com

用户名:dombo  得分:0  时间:2007-12-07 12:44:11
现在正在做和楼主说的功能类似的程序,此贴帮助极大,断断续续看了几天,终于明白了

用户名:xsf322  得分:0  时间:2007-12-05 17:31:07
看到这样大师级人物的帖及回帖,真是惭愧,连上CSDN的勇气都没有了。。

用户名:hixiaosheng  得分:0  时间:2007-12-04 14:59:14
碰到NB人物了,不顶没有天理了。

用户名:jenny_yu  得分:0  时间:2007-12-04 11:19:02
up

用户名:woshishei  得分:0  时间:2007-12-04 11:16:17
为什么给帖子加分的时候,提示错误信息“无权使用此功能”?

用户名:kongweibin  得分:0  时间:2007-12-02 16:24:13
顶一个!!

用户名:jin3long  得分:0  时间:2007-12-02 14:38:08
哪位大哥给一段vb.net连接mdb数据库及执行相关查询的代码,是用windows窗体显示,本人菜鸟,多谢!

用户名:jinghao666666  得分:0  时间:2007-12-01 07:26:04
看贴,帮顶。

用户名:woshishei  得分:0  时间:2007-11-29 17:24:50
Bingo!

再次对boblaw和vwxyzh两大高手的联手拔刀表示万分感激!

用户名:boblaw  得分:0  时间:2007-11-29 17:12:33
但是,我原来的事件响应程序OnEventTimeClock()不知道应该怎么改动一下。  
---------------------------------
VB.NET code
     
     
Private Sub cnt_Reset( ByVal sender As Object , ByVal e As EventArgs) For i As Integer = 0 To objcnt - 1 ' 每开启一个线程,调用一次cnt.Enter(),使计数器自减 cnt.Enter() ' 此处放置OnEventTimeClock的代码 Next cnt.Wait() ' 调用cnt.Wait(),cnt对象在响应完成后将会发出事件通知 End Sub


还有一点,其实阻塞主线程并没有关系的
------------------------------
如果你的主线程确认除了触发事件,不会有其他任务,那么使用vwxyzh的方法,其核心在于使用了异步触发事件,一个BeginInvoke对应一个EndInvoke,如vwxyzh 所述,EndInvoke可以理解成Thread.Join(),如果BeginInvoke调用的过程没有完成,EndInvoke将会阻塞线程,这样可以保证所有的事件响应完成后,调用线程才会继续,这时你可以再触发下一次事件

用户名:vwxyzh  得分:0  时间:2007-11-29 16:36:51
简单的说,我的方法就是利用Custom Event,修改事件的AddHandler、RemoveHandler和RaiseEvent这3个方法。

主要是在RaiseEvent这个方法里面把原来委托默认的按顺序执行变成每一个委托执行BeginInvoke,也就是第一个循环
(BeginInvoke就是往线程池里面添加一个WorkItem,有点像Thread.Start(),不过是加到线程池里面)

后面一个循环要求每个委托都结束异步调用。
(EndInvoke可以理解成Thread.Join(),只不过是对应BeginInvoke的)

最终实现事件处理方法的并行执行。

用户名:woshishei  得分:0  时间:2007-11-29 16:04:03
说白了,多线程事件响应有四个问题现在需要解决:
一、事件响应的过程很长,一个事件处理程序不能阻止后续的事件处理程序,所以要用异步响应方式。
二、主线程需要等待所有CWatcher对象响应完毕才能发出下一个事件,但不能用循环的方式消耗资源。
三、CWatcher对象的数量远大于64个,明显WaitHandle对信号量有限制,需要突破。
四、代码的重用性和可管理性要强,因为需要扩展的类似功能很多。

用户名:woshishei  得分:0  时间:2007-11-29 15:41:24
vwxyzh和boblaw两位的补充对我再次面临的问题很有启发。

因为天资愚笨的关系,vwxyzh的方法不太懂,但好像是在尝试自定义事件异步执行(Which I tried to get better understand but failed),要是有实现的代码就好了,本来就整不太明白。boblaw的方法按照我的理解,需要靠cnt.Wait()来反复触发数量为objcnt的事件,这种方法确实突破了WaitHandle对信号量最多不能超过64的限制。但是,我原来的事件响应程序OnEventTimeClock()不知道应该怎么改动一下。

同时,回到问题的出发点,我认为自己的设计思路可能就不太正确,如果设计多线程事件响应都需要:
RaiseEvent(主线程,在发出事件后同步)->OnEvent(主线程,启用线程池)->OnEventThread(分线程,处理事件)
这种模式的话,感觉这样做程序跳转就已经太多了,阅读性和代码管理性也不强,两位认为呢?

还有一点,其实阻塞主线程并没有关系的,在面向对象的设计中,主线程通常是作为控制线程,主要负责控制仿真周期、剧情事件的触发流程等等,一个仿真周期(或者剧情事件)处理完成之前,其他周期或事件必须处于等待状态,否则就会出异常。其实我在想,能不能修改响应机制,变成:

RaiseEvent(主线程,在发出事件异步执行响应程序)->OnEventThread(在各自的线程中处理事件)

用户名:boblaw  得分:0  时间:2007-11-29 12:50:13
更正一下
MyCounter的FireEvent()方法应声明为Private

用户名:boblaw  得分:0  时间:2007-11-29 12:43:13
楼上的方法是可行的,但是与WaitHandle一样,会导致主线程在等待全部事件响应完成之前一直阻塞.
想了一下,设计了一个MyCounter类来实现,每调用一次Wait,MyCounter类会在所有事件完成后以事件形式通知主线程.主线程收到事件通知后,可以触发下一次事件.不会阻塞主线程,LZ可写代码中止事件的触发.已测试通过
(注:由于时间关系,这个类并不会很完善,但足以应付LZ所述的场景)
VB.NET code
     
     
Public Class MyCounter Private cnt As Integer = 0 Private f As Boolean Private ctl As Control Public Event OnReset As EventHandler Public Sub New ( ByVal owner As Control) ctl = owner End Sub Public Sub Enter() SyncLock Me cnt -= 1 End SyncLock End Sub Public Sub Release() SyncLock Me cnt += 1 If cnt = 0 And f Then Dim cb As New EventHandler( AddressOf FireEvent) ctl.BeginInvoke(cb) f = False cnt = 0 End If End SyncLock End Sub Public Sub Wait() SyncLock Me f = True If cnt = 0 And f Then Dim cb As New EventHandler( AddressOf FireEvent) ctl.BeginInvoke(cb) f = False cnt = 0 End If End SyncLock End Sub Public Sub FireEvent() RaiseEvent OnReset( Me , New EventArgs()) End Sub End Class ' 以上是类的代码 ' 主线程代码 Dim cnt As New MyCounter( Me ) Private Sub Form1_Load( ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase .Load ' 并绑定MyCounter类的OnReset事件,以接收通知 AddHandler cnt.OnReset, AddressOf cnt_Reset cnt.Wait() ' 此行代码,用来最一次触发大量事件用 End Sub ' 此过程用于触发大量事件 Private Sub cnt_Reset( ByVal sender As Object , ByVal e As EventArgs) For i As Integer = 0 To objcnt - 1 ' 每开启一个线程,调用一次cnt.Enter(),使计数器自减 cnt.Enter() System.Threading.ThreadPool.QueueUserWorkItem( New WaitCallback( AddressOf OnEventTimeClockThread)) Next cnt.Wait() ' 调用cnt.Wait(),cnt对象在响应完成后将会发出事件通知 End Sub ' 各个对象响应事件的代码,在响应完成后,需要调用cnt.Release,使计数器自增 Public Sub OnEventTimeClockThread() Threading.Thread.Sleep( 2000 ) cnt.Release() End Sub

用户名:vwxyzh  得分:0  时间:2007-11-29 09:54:54
VB.NET code
     
     
Public Class Foo Private _handlers As New List( Of EventHandler)() Protected Overridable Sub OnBar( ByVal e As EventArgs) RaiseEvent Bar( Me , e) End Sub Public Custom Event Bar As EventHandler AddHandler ( ByVal value As EventHandler) _handlers.Add(value) End AddHandler RemoveHandler ( ByVal value As EventHandler) _handlers.Remove(value) End RemoveHandler RaiseEvent ( ByVal sender As Object , ByVal e As System.EventArgs) SyncLock Me Dim ars(_handlers.Count) As IAsyncResult For i As Integer = 0 To _handlers.Count - 1 ars(i) = _handlers(i).BeginInvoke(sender, e, Nothing , Nothing ) Next For i As Integer = 0 To _handlers.Count - 1 _handlers(i).EndInvoke(ars(i)) Next End SyncLock End RaiseEvent End Event End Class


用户名:boblaw  得分:0  时间:2007-11-29 09:32:46
刚查了MSDN,的确有这个问题,WaitHandles数组成员不能超过64个.

用户名:woshishei  得分:0  时间:2007-11-28 22:20:51
一直到今天中午,运行结果一切正常。

下午突然又发现了一个新问题,就是当创建的CWatcher对象数量超过64的时候,提示错误信息“WaitHandles 的数目必须少于或等于 64 个。”指向这一句:Threading.WaitHandle.WaitAll(WaitHandleArray)

程序是用作仿真的,因此对象个数是以千为单位的数量级,如果最多只能建立64个的话,那就太少了点。请问这个问题又该怎么解决呢?

用户名:woshishei  得分:0  时间:2007-11-28 11:57:53
非常感谢boblaw黑马王子(经典,由我制造),感谢你的帮助让我打消了了同时使用多线程与面向对象设计方法的疑虑。

用户名:boblaw  得分:0  时间:2007-11-28 11:42:48
創建一個Module,寫一個Sub Main過程,如下(衹寫了重要的代碼)
VB.NET code
     
     
Module Module1 < MTAThread() > _ Sub Main() Dim frm As New Form1 Application.Run(frm) End Sub End Module

設置項目屬性,在“應用程序”選項卡中,取消啟用應用程序框架,選擇啟動對象為Sub Main().

用户名:woshishei  得分:0  时间:2007-11-28 11:16:00
我做的是窗口程序,执行代码从
  Private Sub StartProc_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
开始的,<MTAThread()>应该放在哪里呢?

用户名:boblaw  得分:0  时间:2007-11-28 10:50:04
<MTAThread()> _
Sub Main()
End Sub

用户名:woshishei  得分:0  时间:2007-11-28 10:41:49
修改运行之后提示指向这句的错误信息:

  Threading.WaitHandle.WaitAll(wharray)

不支持一个 STA 线程上针对多个句柄的 WaitAll。

这个信息我昨天尝试的时候也出现过,可是还没找到解决办法。

用户名:boblaw  得分:0  时间:2007-11-27 21:23:43
VB.NET code
     
     
' 主线程声明一个ArrayList,用来保存AutoResetEvent ' 其实使用数组最方便,但数组个数不确定 ' 由于AutoResetEvent是在各个线程中加入ArrayList,确保线程安全,应使用Synchronized封装 Dim whs As ArrayList = ArrayList.Synchronized( New ArrayList()) ' 下面是触发事件的代码 While True ' LZ写代码触发事件RaiseEvent EventTimeClock Dim wharray(whs.Count - 1 ) As WaitHandle whs.CopyTo(wharray) WaitHandle.WaitAll(wharray) ' 触发事件后等待事件全部执行完成 End While ' 響應事件時,按如下方式響應,将AutoResetEvent设置为false,再放入線程池 Public Sub OnEventTimeClock( ByVal sender As Object , ByVal e As EventArgs) Dim are As New AutoResetEvent( False ) whs.Add(are) System.Threading.ThreadPool.QueueUserWorkItem( New WaitCallback( AddressOf OnEventTimeClockThread), are) End Sub ' 結束事件處理時,将AutoResetEvent设置为true Public Sub OnEventTimeClockThread( ByVal state As Object ) Dim are As AutoResetEvent = CType (state, AutoResetEvent) Threading.Thread.Sleep( 2000 ) are.Set() ' 将AutoResetEvent设置为true End Sub

用户名:boblaw  得分:0  时间:2007-11-27 21:20:57
WaitHandle.WaitAll()也是.net中一种同步技术,其实很适合这种情况,使用之后,在上面的所有担心都不存在了.
但是会使用你的主线程失去响应.因为主线程必须在所有的AutoResetEvent设置为True后才继续执行.
使用方法如下:
VB.NET code
     
     
' 主线程声明一个ArrayList,用来保存AutoResetEvent ' 其实使用数组最方便,但数组个数不确定 ' 由于AutoResetEvent是在各个线程中加入ArrayList,确保线程安全,应使用Synchronized封装 Dim whs As ArrayList = ArrayList.Synchronized( New ArrayList()) ' 下面是触发事件的代码 While True ' LZ写代码触发事件RaiseEvent EventTimeClock Dim wharray(whs.Count - 1 ) As WaitHandle whs.CopyTo(wharray) WaitHandle.WaitAll(wharray) ' 触发事件后等待事件全部执行完成 End While ' 響應事件時,按如下方式響應,先遞增計算器,再放入線程池 Public Sub OnEventTimeClock( ByVal sender As Object , ByVal e As EventArgs) Dim are As New AutoResetEvent( False ) whs.Add(are) System.Threading.ThreadPool.QueueUserWorkItem( New WaitCallback( AddressOf OnEventTimeClockThread), are) End Sub ' 結束事件處理時,計算器自增 Public Sub OnEventTimeClockThread( ByVal state As Object ) Dim are As AutoResetEvent = CType (state, AutoResetEvent) Threading.Thread.Sleep( 2000 ) are.Set() ' 将AutoResetEvent设置为true End Sub

用户名:woshishei  得分:0  时间:2007-11-27 18:18:57
谢谢boblaw,其实我一直在尝试ManualResetEvent事件这种方式,需要调用WaitHandle.WaitAll()方法

据说这样调用的话就不用通过主动循环等待事件响应,可以节省主线程资源。这种方式你会吗?

用户名:boblaw  得分:0  时间:2007-11-27 17:46:17
另外,我考虑有可能会在中途出现Counter=RCounter的情况,这样主线程就会意外跳过去。你觉得呢?
-----------------------
我認為是不可能的。主線程優先執行,相對而言線程池的效率不高,主線程很快就觸發完了事件,而線程池可能才剛開始工作。

用户名:boblaw  得分:0  时间:2007-11-27 17:36:31
1.
SyncLock syncobj1  
  RCounter += 1  
End SyncLock
這是同步,當一個線程在訪問syncobj1時,其他線程不可以訪問syncobj1,以防止出現計數器計數錯誤的情況

2.
这个应该是While语句吧
-------------------
使不使用while是你決定,因為你沒有告訴我是如何觸發事件的,所以我沒有使用循環.

3.
这个应该是While语句吧,这样做了消息循环之后,主线程的资源就被占用了,
-----------------------
看你如何觸發事件嘍,如何你不停主動循環去檢查是否可以觸發事件,那就資源消耗很大了。
給你個建議,每次完成處理之後,先檢查Counter是否=RCounter ,如果等於就發送一個windows消息(調用win32 API sendmessage)給主窗體,由主窗體來觸發下一個事件。
這樣對主窗體來說,是被動地去觸發事件,性能會好很多。

用户名:woshishei  得分:0  时间:2007-11-27 17:29:32
另外,我考虑有可能会在中途出现Counter=RCounter的情况,这样主线程就会意外跳过去。你觉得呢?

用户名:woshishei  得分:0  时间:2007-11-27 17:21:31
好像是可以了,我还在验证结果。syncobj 和 counter 我都设成了静态变量。

SyncLock syncobj1
  RCounter += 1
End SyncLock
这一段我不明白什么意思,能不能介绍一下呢。

If Counter=RCounter Then
  Counter=0;
  RCounter=0;
  '觸發新事件
Else
  '不觸發
End If
这个应该是While语句吧,这样做了消息循环之后,主线程的资源就被占用了,不便于以后响应窗口。我听说有manuresetevent可以用,不知道怎么实现。

用户名:boblaw  得分:0  时间:2007-11-27 16:31:13
按上面的處理方法,應該可以實現你的要求

用户名:boblaw  得分:0  时间:2007-11-27 16:28:30
VB.NET code
     
     
' 主線程中聲明兩個計算器,和一個對象(用來同步) Dim counter As Integer = 0 ' 表示已經響應的個數 Dim RCounter As Integer = 0 ' 表示需要響應的個數 Dim syncobj1 As New Object () Dim syncobj2 As New Object () ' 要觸發事件前,先判斷兩個計數器是否相等,如果相等,表示已經全部響應,可以觸發下一次事件 ' 觸發之前,還需要將計數器清0 If Counter = RCounter Then Counter = 0 ; RCounter = 0 ; ' 觸發新事件 Else ' 不觸發 End If ' 響應事件時,按如下方式響應,先遞增計算器,再放入線程池 Public Sub OnEventTimeClock( ByVal sender As Object , ByVal e As EventArgs) SyncLock syncobj1 RCounter += 1 End SyncLock // 調用你原來的代碼,啟用ThreadPool... End Sub ' 結束事件處理時,計算器自增 Public Sub OnEventTimeClockThread( ByVal sender As Object ) Threading.Thread.Sleep( 2000 ) SyncLock syncobj2 Counter += 1 End SyncLock End Sub

用户名:woshishei  得分:0  时间:2007-11-27 16:05:06
嗯,你说对了,我必须要使用线程池进行并发响应,所以我就遇到了这个问题

  Public Sub OnEventTimeClock(ByVal sender As Object, ByVal e As EventArgs)

  Dim GoOnEventTimeClock As New Threading.WaitCallback(AddressOf OnEventTimeClockThread)
  Threading.ThreadPool.QueueUserWorkItem(GoOnEventTimeClock, sender)
  Console.WriteLine("Involk" & vbTab & Me.num.ToString _
 & vbTab & "IsThreadPool" & vbTab & Threading.Thread.CurrentThread.IsThreadPoolThread.ToString _
 & vbTab & "ThreadID" & vbTab & Threading.Thread.CurrentThread.ManagedThreadId _
 & vbTab & sender.ThisTime.ToString)

  End Sub

  Public Sub OnEventTimeClockThread(ByVal sender As Object)

  Threading.Thread.Sleep(2000)

  End Sub

用户名:boblaw  得分:0  时间:2007-11-27 15:51:43
事件的觸發,與是否處在多線程環境關無關係,事件觸發的代碼會在觸發事件的那個線程中執行
RaiseEvent一定會等待所以的響應完成之後才運行後面的代碼
除非你在響應事件的過程中寫代碼另外再開啟線程來處理,但這樣做就沒有意義了。

用户名:boblaw  得分:0  时间:2007-11-27 15:36:53
現在想想,觸發一個事件,必然會等事件被響應完成後才繼續運行後面的代碼,不需要考慮你所說的這個問題啊。
難道你是在響應事件的過程中使用了多線程?能否大概看下你觸發事件的代碼,以及響應事件的代碼?

用户名:woshishei  得分:0  时间:2007-11-27 15:06:20
不好意思,我说的10000个是指很多,而不是确定的某个数。
因为这是一个仿真程序。对象数量实际上是不确定的,也就是说,有的可能会响应,而有的不会。
关键的问题在于,主线程如何等待第一个事件被所有对象响应之后再继续raise第二个事件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值