2017-07-17 121 views
-1

我已經知道模塊級變量的值不會被改變,直到改變它的子程序退出。vb.net子程序沒有更新模塊級別的變量,直到退出sub

 StopBackgroundWorker1 = True 
    Thread.Sleep(1500) 

    If BackgroundWorker1Complete = False Then 
     Exit Sub 
    End If 

在這個例子中,我加了一個長時間的測試。我只是試圖停止並開始安全地使用vb 2017新背景工人類的後臺工作。

上面的例子中帶有「StopBackgroundWorker1 = True」的例子,我希望在安全的地方阻止工作人員,然後在其他代碼中繼續使用該子工作。

但是,發生的事情是,「StopBackgroundWorker1 = True」沒有被設置爲「True」,直到子退出。

必須有另一種方式做我想做的事,請大家幫忙

確定這裏是一個完整的例子,

Imports System.ComponentModel 
    Imports System.Threading 



    Public Class Form1 


Private flag As Boolean = False 
Dim Completed As Boolean = False 


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 

    BackgroundWorker1.RunWorkerAsync() 
End Sub 

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 


    flag = True 

    'Do 
    ' do loop never see's a true flag 
    'Loop Until Completed 

    Thread.Sleep(500) 

    If Completed = True Then 
     Label1.BackColor = Color.Red 
    End If 

End Sub 

Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork 

    Do 
     Thread.Sleep(25) 
    Loop Until flag 


End Sub 


Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As 
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted 
    Completed = True 
End Sub 

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles 
Button2.Click 
    Label1.Text = flag.ToString 
End Sub 
End Class 

現在的概念是,如果你打Button1的等待背景工人要完成,它應該變成紅色。但事實並非如此。執行循環尋找一個真正的國旗將旋轉永遠鎖定形式。

我已經確定這個例子,標誌沒有設置爲true,直到你退出子。再次點擊Button1,lable1變成紅色。

在此先感謝您的任何答案。

+0

您在哪個級別聲明變量'StopBackgroundWorker1'? –

+0

你有一些誤解。首先,VB 2017沒有新的BackgroundWorker類。 'BackgroundWorker'已經可用於.NET 2.0以來的所有.NET語言,這與VB 2005的發佈一致。 – jmcilhinney

+0

其次,您不會啓動和停止'BackgroundWorker'。你調用'RunWorkerAsync'方法和'BackgroundWorker'只是引發一個事件。該事件由您的表單處理,「DoWork」事件處理程序是表單的一部分,而不是「BackgroundWorker」。 – jmcilhinney

回答

1

這並不回答你的問題本身,但我想發佈一個大的代碼片段,所以我會張貼它作爲答案。它表明,您認爲問題不是問題,即即使您更改了該字段的值,但即使該更改是由事件處理程序創建的,該字段的值也會發生更改。

創建一個新的Windows窗體應用程序項目,在窗體的默認代碼添加Button,一個LabelBackgroundWorker到窗體,然後粘貼在此代碼:

Imports System.ComponentModel 
Imports System.Threading 

Public Class Form1 

    Private flag As Boolean 

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
     BackgroundWorker1.WorkerReportsProgress = True 
     BackgroundWorker1.RunWorkerAsync() 
    End Sub 

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 
     Label1.Text = flag.ToString() 
    End Sub 

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork 
     Thread.Sleep(5000) 

     flag = True 
     BackgroundWorker1.ReportProgress(0) 

     Thread.Sleep(5000) 
    End Sub 

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged 
     Label1.BackColor = Color.Green 
    End Sub 

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted 
     Label1.BackColor = Color.Red 
    End Sub 

End Class 

運行該項目,並當表單出現時,以每秒幾次的速度開始點擊Button。您將會看到Label上顯示的flag字段的值從False更改爲True,只要在DoWork事件處理程序中執行設置代碼的代碼即可。發生這種情況時,Label會變綠,所以很容易發現。你會知道它不會等到DoWork事件處理程序完成,因爲Label將在此時變成紅色。

編輯:現在你已經提供了所有的相關信息,問題很明顯。正如我已經說過的那樣,當你設置一個變量時,那就是該變量的值。沒有等待,因爲不能等待,因爲沒有地方爲變量存儲臨時值。

它看起來不然的原因是您的測試代碼有問題。如果你使用調試器,那麼你會看到如何。當您使用BackgroundWorker時,DoWork事件處理程序在輔助線程上執行,但RunWorkerCompleted事件處理程序在UI線程上執行。這意味着您的DoWork事件處理程序可以與您的Button1的事件處理程序同時執行,因爲它們在不同的線程上,但RunWorkerCompleted事件處理程序不能同時運行,所以它必須等到Click事件處理程序完成纔可以執行。這意味着設置Completed字段的代碼在Click事件處理程序完成之前不會執行。這並不是說字段值在設置時不會改變,而是它實際上沒有設置。如果您在訪問該Completed字段的兩行上放置斷點,則會看到該字段。

您正在做的錯誤是在DoWork事件處理程序完成後嘗試在該事件處理程序中執行某些操作。這是錯誤的。這正是RunWorkerCompleted事件處理程序的用途。這是您在後臺工作完成後執行UI工作的地方。

此外,你可以擺脫那flag變量。 BackgroundWorker課程內置取消功能。請看CancelAsync方法和CancellationPending屬性。

+0

謝謝你的例子!我將運行該示例,然後在「Label1.Text = flag.ToString()」之後添加一個thread.sleep,並且我應該獲得與我的項目看到的結果相同的結果。我的意圖是在我的項目中等待「RunWorkerCompleted」標誌並繼續執行同一子程序中的其他工作。 – Jimva

+0

更改我的第一條評論,您正在更改BackgroundWorker1線程內的標誌。我所做的是完全相反的。從一個按鈕更改變量並嘗試從線程內讀取它。將改變您的示例來演示。 – Jimva

+0

感謝您的解釋,在此實驗之後,我得出結論:事件處理程序必須等待主線程。我已經嘗試過使用「CancelAsync」獲得相同的結果。你解釋得很好,謝謝你。 – Jimva

0

非常感謝「jimcilhinney」的見解!我已經找到了我正在尋找的代碼!

此代碼允許我通過允許後臺線程完全完成重啓之前安全地停止和啓動後臺線程。

在後臺線程停止的時候,用戶操作可以執行操作,而不用擔心跨線程或thread.abort亂碼的情況。

最後,無壓力螺紋!

我希望我可以在MSDN上找到我讀過的文檔,指出await async方法遠遠優於task.run,但這是另一個參數。

這可能不是世界上最好的代碼,但它的工作原理!

鑑於我試圖用我的項目中的所有代碼重寫異步並等待,我會堅持下去!

Public Class Form1 


Private flag As Boolean = False 
Dim Completed As Boolean = False 


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
    BackgroundWorker1.RunWorkerAsync() 
End Sub 

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 

    flag = True 

    Await Task.Run(Sub() 
         Do 
         Loop Until Completed 
        End Sub) 

    If Completed = True Then 
     Label1.BackColor = Color.Red 
    End If 

    BackgroundWorker1.RunWorkerAsync() 

End Sub 


Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork 

    Do 
     Thread.Sleep(250) 
    Loop Until flag 


End Sub 


Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As 
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted 
    Completed = True 
End Sub 

End Class 
0

對於所有的學者來說,這裏的代碼更合適。

Imports System.ComponentModel 
Imports System.Threading 


Public Class Form1 

Private Completed As Boolean = False 


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
    BackgroundWorker1.WorkerSupportsCancellation = True 
    BackgroundWorker1.RunWorkerAsync() 
End Sub 

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles 
Button1.Click 

    BackgroundWorker1.CancelAsync() 

    Await Task.Run(Sub() 
         Do 
         Loop Until Completed 
        End Sub) 

    If Completed = True Then 
     Label1.BackColor = Color.Red 
    End If 

    BackgroundWorker1.RunWorkerAsync() 

End Sub 


Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork 

    Do 
     Thread.Sleep(250) 
    Loop Until BackgroundWorker1.CancellationPending 


End Sub 


Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted 
    Completed = True 
End Sub 

End Class 
+0

我一直在研究這個問題。如何以絕對安全的方式啓動,停止和重啓後臺線程。最後,在這裏! – Jimva

+0

愚蠢的我必須設置「完成」爲false之前重新啓動線程! – Jimva