交叉线程异常和调用所需的解决方案不会更改我的控件值



编辑解决方案 :

在这里,我在每个对象中设置我的byref值,然后我正在运行后台工作者

Private Sub TelechargeFichier()
    Dim DocManquant As Boolean = False
    Dim docName As String = ""
    Dim lg As String = ""
    Dim telechargementFini As Boolean = False
    lblMessage.Text = EasyDealChangeLanguage.Instance.GetStringFromResourceName("1478")
    prgBar.Maximum = m_listeFichiers.Count
    For i As Integer = 0 To m_listeFichiers.Count - 1
        m_listeFichiers(i).Set_ByRefLabel(lblMessage)
        m_listeFichiers(i).Set_ByRefPrgbar(prgBar)
        m_listeThreads.Add(New Thread(AddressOf m_listeFichiers(i).DownloadMe))
    Next

    m_bgWorker = New BackgroundWorker
    m_bgWorker.WorkerReportsProgress = True
    AddHandler m_bgWorker.DoWork, AddressOf DownloadFiles
    m_bgWorker.RunWorkerAsync()



    ''Completed
    'lblMessage.Text = EasyDealChangeLanguage.Instance.GetStringFromResourceName("1383")
    'Me.DialogResult = System.Windows.Forms.DialogResult.OK
End Sub

这是我的下载文件功能:请注意,每次启动都将执行 downloadMe 功能,如下所示

  Private Sub DownloadFiles(sender As Object, e As DoWorkEventArgs)

    For i As Integer = 0 To m_listeThreads.Count - 1
        m_listeThreads(i).Start()
    Next
    For i As Integer = 0 To m_listeThreads.Count - 1
        m_listeThreads(i).Join()
    Next

End Sub

问题在这里:

我有多个线程,每个线程将下载一个ftp文件。我希望每个已完成的文件都会为进度条设置一个值,并从我的 UI 线程中设置一个标签。 出于某种原因,调用要求永远不会更改为 false。

这是我启动所有线程的小函数

 Private Sub TelechargeFichier()
    Dim DocManquant As Boolean = False
    Dim docName As String = ""
    Dim lg As String = ""
    Dim telechargementFini As Boolean = False
    lblMessage.Text = EasyDealChangeLanguage.Instance.GetStringFromResourceName("1478")
    prgBar.Maximum = m_listeFichiers.Count
    For i As Integer = 0 To m_listeFichiers.Count - 1
        m_listeFichiers(i).Set_ByRefLabel(lblMessage)
        m_listeFichiers(i).Set_ByRefPrgbar(prgBar)
        m_listeThreads.Add(New Thread(AddressOf m_listeFichiers(i).DownloadMe))
    Next

    For i As Integer = 0 To m_listeThreads.Count - 1
        m_listeThreads(i).Start()
    Next
    For i As Integer = 0 To m_listeThreads.Count - 1
        m_listeThreads(i).Join()
    Next
    'Completed
    lblMessage.Text = EasyDealChangeLanguage.Instance.GetStringFromResourceName("1383")
    Me.DialogResult = System.Windows.Forms.DialogResult.OK
End Sub

这是我从 UI 线程保存 Byref 控件的属性。这是我的对象,其中内容地址函数将下载文件(下载我)

    Public Sub Set_ByRefPrgbar(ByRef prgbar As ProgressBar)
    m_prgBar = prgbar
End Sub
Public Sub Set_ByRefLabel(ByRef lbl As EasyDeal.Controls.EasyDealLabel3D)
    m_lblMessage = lbl
End Sub

这是下载功能:

   Public Sub DownloadMe()
    Dim ftpReq As FtpWebRequest
    Dim ftpResp As FtpWebResponse = Nothing
    Dim streamInput As Stream
    Dim fileStreamOutput As FileStream
    Try
        ftpReq = CType(WebRequest.Create(EasyDeal.Controls.Common.FTP_CONNECTION & m_downloadFtpPath & m_filename), FtpWebRequest)
        ftpReq.Credentials = New NetworkCredential(FTP_USER, FTP_PASS)
        ftpReq.Method = WebRequestMethods.Ftp.DownloadFile
        ftpResp = ftpReq.GetResponse
        streamInput = ftpResp.GetResponseStream()
        fileStreamOutput = New FileStream(m_outputPath, FileMode.Create, FileAccess.ReadWrite)
        ReadWriteStream(streamInput, fileStreamOutput)
    Catch ex As Exception
        'Au pire la fichier sera pas downloader
    Finally
        If ftpResp IsNot Nothing Then
            ftpResp.Close()
        End If
        Dim nomFichier As String = m_displaynameEN
        If EasyDealChangeLanguage.GetCurrentLanguageTypes = EasyDealChangeLanguage.EnumLanguageType.Francais Then
            nomFichier = m_displaynameFR
        End If
        If m_lblMessage IsNot Nothing Then
            EasyDealCommon.TH_SetControlText(m_lblMessage, String.Format(EasyDealChangeLanguage.Instance.GetStringFromResourceName("1479"), nomFichier))
        End If
        If m_prgBar IsNot Nothing Then
            EasyDealCommon.TH_SetPrgValue(m_prgBar, 1)
        End If
    End Try

End Sub

这是交叉线程调用解决方案函数:

    Public Sub TH_SetControlText(ByVal ctl As Control, ByVal text As String)
    If ctl.InvokeRequired Then
        ctl.BeginInvoke(New Action(Of Control, String)(AddressOf TH_SetControlText), ctl, text)
    Else
        ctl.Text = text
    End If
End Sub
Public Sub TH_SetPrgValue(ByVal prg As ProgressBar, ByVal value As Integer)
    If prg.InvokeRequired Then
        prg.BeginInvoke(New Action(Of ProgressBar, Integer)(AddressOf TH_SetPrgValue), prg, value)
    Else
        prg.Value += value
    End If
End Sub

问题是调用要求永远不会达到 false,它实际上进入 beginInvoke 但永远不会在 Else 部分结束以设置值。

最初的方法 TelechargeFichier() 是从哪里发射的? 它是在主 UI 线程本身中吗?

如果是这样,那么这部分是一个问题:

For i As Integer = 0 To m_listeThreads.Count - 1
    m_listeThreads(i).Join()
Next

这是冻结主线程,直到其他线程完成。 这符合您的症状"它实际上进入开始调用,但永远不会在 Else 部分结束以设置值。 由于主 UI 线程被冻结,等待其他线程完成,它不可能执行您对 BeginInvoke() 的请求。

如果要使用 Join() 并等待其他线程完成,请在 BackgroundWorker() 或其他不是主 UI 线程的合适线程的 DoWork() 处理程序中执行此操作。

有了invoke,你想"回到"UI线程中,在那里做一些工作。您是否查看过您的 UI 线程的作用?它启动线程,然后使用 .Join .这将阻止 UI 线程,直到所有其他线程完成。这意味着您的更新代码无法运行(即使在 begininvoke 之后),因为被阻止的 (UI) 线程不会运行它;)

Public Class Form1
Private tm As New Threading.Timer(AddressOf tmcallback)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    tm.Change(0, 500)
    Dim t As New Threading.Thread(Sub() Threading.Thread.Sleep(10000))
    t.Start()
    t.Join()
    tm.Change(-1, -1)

End Sub
Sub tmcallback()
    If Me.InvokeRequired Then
        Console.WriteLine("Required")
        Me.BeginInvoke(Sub() tmcallback())
    Else
        Console.WriteLine("NOT Required")
    End If
End Sub
End Class

请注意输出:首先显示所有"必需",只有在 UI 线程取消阻止后,才会出现所有"不需要"。

相关内容