none
請問關於ThreadPool.QueueUserWorkItem 的問題 RRS feed

  • 問題

  • 我在參考Bill Chung
    http://social.msdn.microsoft.com/Forums/zh-TW/233/thread/03a6e821-7660-4c53-a07b-3c1a8db91eef

    自己寫一個程式   有兩個按鈕 
    connect  執行
    disconnect 暫停

    Private Sub Connect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Connect.Click
     If Even_Start = True Then
      I2C_READ_VALUE.Reset()
     Else
      ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Running), I2C_READ_VALUE)
     End If
    End Sub

    Private Sub Disconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Disconnect.Click
      I2C_READ_VALUE.WaitOne()
    End Sub

     Sub Running(ByVal state As Object)
       Even_Start = True
       CType(state, ManualResetEvent).Set()
     End SuB

    我的想法是一開始按connect 執行
    ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Running), I2C_READ_VALUE)
    按暫停   I2C_READ_VALUE.WaitOne()
    再按執行  I2C_READ_VALUE.Reset()

    2009年10月1日 上午 05:42

解答

  • OK...我們來個簡單的假設
    myResetEvent是一個ManualResetEvent的執行個體 (用全域)
    (以你的例子, 建構函式的參數要傳True, 你用False可能就真的一開始就停住了 )
    有兩個會同時跑的執行緒 f1 與 f2
    因為你要停止的是f1
    所以你的f1 function內應該有一大堆的程式碼
    你可以在程式碼中夾入 myResetEvent.WaitOne(例子中是一個, 其實你可以夾一大堆, 如果程式碼很長的話,但要慎選位置)
    比方這樣
    public function f1()

    While True
    進行Socket通訊
    .....
    myResetEvent.WaitOne()
    End


    End function

    public Function f2()
    (1)進入程序的第一件事情, 先呼叫myResetEvent.Reset, 讓f1中的程序會在myResetEvent.WaitOne()中暫止
    (2)System.Threading.Thread(200) . 停一下, 讓f1中的程序能執行到達 myResetEvent.WaitOne()的位置
    (3)開始做你f2本來該做的事情
    (4)在該做的事情做完後, 呼叫myResetEvent.Set 讓f1的程序從myResetEvent.WaitOne()的下一行繼續
    End Function
    ====補充一下=================================
    這種方法就不用設兩個按鈕控制了, 反正你只要在Button.Click中去呼叫f2程序 (這個其實不需要寫在多緒了) , f2程序中就去做暫止與重啟f1執行緒的動作就好了.


    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年10月1日 下午 04:44
    版主

所有回覆

  • Reset和Set不一樣
    要讓WitOne()解除, 要用Set.你用Reset反而會讓它停住
    在MSDN文件庫中
    [ManualResetEvent. Reset 方法 ] 內容的說明是:將事件的狀態設定為未收到信號,會造成執行緒封鎖。

    [ManualResetEvent.Set 方法 ]將事件的狀態設定為未收到信號,讓一個或多個等候執行緒繼續執行。( 我覺得這句中文翻錯了, 因為原文是 Sets the state of the specified event to signaled. 中文應該是指已收到訊號 )

    CType(state, ManualResetEvent).Set() 這個啊, 因為我不是用全域的ManualResetEvent類別執行個體, 所以是用參數型式傳入方法, 可以參考以下程式粗體字的部份
    因為只能用Object型式傳, 所以我用CType把 State這個物件轉型回ManualResetEvent類別

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
            Dim myResetEvent As New ManualResetEvent(False)  
            Dim j As Integer 
            For j = 0 To 5 
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
                myResetEvent.WaitOne() 
            Next 
        End Sub 
        Private Sub Test(ByVal state As Object) 
            MessageBox.Show(i) 
            i = i + 1 
            CType(state, ManualResetEvent).Set() 
        End Sub

    題外話, 上次那個ByRef的你有測過嗎? 結果應該正常了吧 ?

    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年10月1日 上午 05:51
    版主
  • 上次那一個是沒問題了
    我剛把打錯的  改成set   也還是不會有動做  << 我剛看了一下  我是按照你的button3的喔  你在那也是用Reset



    我在問一下  myResetEvent  它是以什麼為標準當做事件訊號
    是當我下 myResetEvent.WaitOne()
    它就在等待我下 CType(state, ManualResetEvent).Set()  才會繼續嗎
    2009年10月1日 上午 06:20
  • 我們先從建構函式來看
    Dim myResetEvent As New ManualResetEvent(False)  
    參數的意義是這樣
    如果初始狀態設定為信號狀態,為 true ;初始狀態設定為非信號狀態則為 false
    其實所謂的信號是存在於這個
    myResetEvent.
    我猜應該是在這個類別的內部有一個私有(Private)欄位(應該就是個變數)有存放這個狀態
    可是因為是Class類別中的Private, 所以我們從外部看不到也摸不著 (如果改寫成Public 屬性就會看到了)
    所以只能使用 Set, Reset方法去變更這個私有欄位
    當你Set, 它就是true , 當你Reset, 它就變False
    當myResetEvent使用WaitOne方法時, 它就檢查這個值是否為True , 如果True則繼續執行, 如果False則等待
    那當WaitOne在等待時, 如果使用了Set方法, 它應該會在執行個體內部引發一個事件或通知, 使得WaitOne方法重新讀取這個訊號, 然後讓確認為True則讓程式繼續執行.
    基本上我沒見過 這個類別的內部程式碼, 狀況是以自己設計類別的狀況來猜測. 不過我想應該差不多就是這麼一回事.

    ===補充========
    可能前幾篇的文章不清楚
    應該這樣說
    WaitOne是你要停止的地方
    而你是靠 myResetEvent.Set , myResetEvent.Reset來控制它要不要停在WaitOne那個位置
    ===============================================
    以之前的例子來說
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
            Dim myResetEvent As New ManualResetEvent(False)  
            Dim j As Integer 
            For j = 0 To 5 
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
                myResetEvent.WaitOne() 
            Next 
        End Sub 
        Private Sub Test(ByVal state As Object) 
            MessageBox.Show(i) 
            i = i + 1 
            CType(state, ManualResetEvent).Set() 
        End Sub

    當J=0
    進入迴圈後遇到ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
    它就跑去執行 Sub Test .
    這個ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
    下一行是 WaitOne, 迴圈就會在這邊暫停
    此時Sub Test就做它的事, 直到 CType(state, ManualResetEvent).Set()  就會讓原迴圈中的myResetEvent.WaitOne()  收到True訊號, 進入到Next 然後又J=1

    所以說, 你的程式要停在哪 就要把WaitOne放在哪.
    ===============再補充一下=============
    你的程式碼
    Private Sub Disconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Disconnect.Click
      I2C_READ_VALUE.WaitOne()
    End Sub
    它是停在Dissconnect_Click這個Sub裡
    並不會去停止其它的程式
    ===========================
    然後, 我實在看不懂  "暫停執行緒" 這件事在這個程式的意義是什麼, 你可能要解釋你程式的原意 (也許要更接近完整的Code) , 才比較容議告訴你, 這幾的方法應該放在你程式中的哪邊才是對的


    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年10月1日 上午 06:38
    版主
  • 我是用多執行緒     總共有兩個執行緒
    一個在專門執行一個是I2C通訊裝置 在做通訊
    另一個是控制GPIB 和I2C
    因為我怕第二個執行緒同樣去呼叫到I2C  所以必需要去讓第一個執行緒做停止
    等完必後在去讓第一個啟動


    我目前這樣子講好了  我要一個多執行緒 是可以一個按鈕讓它暫停  另一個按鈕按下去又開始啟動 
    那個執行緒是跑在一個叫RUNNING的副程式內

    2009年10月1日 下午 04:19


  • 那如果沒WaitOne
    它會在
    Button1_Click  & Test 這兩個副程式內分兩個執行緒跑嗎
    不然PC指到副程式也會把PC指到副程式內

    我對你講的方法認為
    ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
    就一定接一個WaitOne
    在進去TEST  如果沒執行到CType(state, ManualResetEvent).Set()
    它就會停止   不然就會繼續下去

    我要的功能是能停止  並我還要它能繼續接下去的動作

    感謝你的回覆




    當J=0
    進入迴圈後遇到ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
    它就跑去執行 Sub Test .
    這個ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Test), myResetEvent
    下一行是 WaitOne, 迴圈就會在這邊暫停
    此時Sub Test就做它的事, 直到 CType(state, ManualResetEvent).Set()  就會讓原迴圈中的myResetEvent.WaitOne()  收到True訊號, 進入到Next 然後又J=1
    2009年10月1日 下午 04:34
  • OK...我們來個簡單的假設
    myResetEvent是一個ManualResetEvent的執行個體 (用全域)
    (以你的例子, 建構函式的參數要傳True, 你用False可能就真的一開始就停住了 )
    有兩個會同時跑的執行緒 f1 與 f2
    因為你要停止的是f1
    所以你的f1 function內應該有一大堆的程式碼
    你可以在程式碼中夾入 myResetEvent.WaitOne(例子中是一個, 其實你可以夾一大堆, 如果程式碼很長的話,但要慎選位置)
    比方這樣
    public function f1()

    While True
    進行Socket通訊
    .....
    myResetEvent.WaitOne()
    End


    End function

    public Function f2()
    (1)進入程序的第一件事情, 先呼叫myResetEvent.Reset, 讓f1中的程序會在myResetEvent.WaitOne()中暫止
    (2)System.Threading.Thread(200) . 停一下, 讓f1中的程序能執行到達 myResetEvent.WaitOne()的位置
    (3)開始做你f2本來該做的事情
    (4)在該做的事情做完後, 呼叫myResetEvent.Set 讓f1的程序從myResetEvent.WaitOne()的下一行繼續
    End Function
    ====補充一下=================================
    這種方法就不用設兩個按鈕控制了, 反正你只要在Button.Click中去呼叫f2程序 (這個其實不需要寫在多緒了) , f2程序中就去做暫止與重啟f1執行緒的動作就好了.


    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年10月1日 下午 04:44
    版主
  • 我今天照你講的方法去試了一遍後
    發現我對的它認知有錯誤
    我的想法是 它在遇到waitone時 應該會去看一下myResetEvent 是被設為.Reset   
    等設為  myResetEvent.set 就會在從waitone 的下一步在走
    我指的是 myResetEvent.Rest 跟 myResetEvent.set在不同的Function
    所以我把它寫成
    Private Sub Connect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Connect.Click
     If Even_Start = True Then
      I2C_READ_VALUE.set()
     Else
      I2C_READ_VALUE.set()
      ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Running), I2C_READ_VALUE)
     End If
    End Sub
    Private Sub Disconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Disconnect.Click
      I2C_READ_VALUE.Reset
    End Sub
    可是結果並不是這樣
    它在I2C_READ_VALUE.Reset()
    如果等不到 I2C_READ_VALUE.set()
    就整個停止了
    你之後在下I2C_READ_VALUE.set()  也不會從waitone繼續下去

    它是因為等待時間太久所以整個放棄嗎
    是不是在waitone(把delay時間改久點 改成-1) 它就能繼續動作

    2009年10月2日 上午 02:45
  • WaitOne應該不會有等太久以致於醒不過來的問題. 可能要注意檢查一下UI是否有接到Button.Click訊號
    這個方式我在系統上面實作過(其實我寫的東西和你很像, 我是實做在門禁系統上), 沒有發生這樣的現象
    不過如果要避免等太久, WaitOne就要用其它多載函式
    [WaitHandle.WaitOne 方法 (Int32, Boolean) ]
    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年10月2日 上午 03:23
    版主