none
Почему не все события отрабатывают? RRS feed

  • Вопрос

  • Приветствую!

    Когда я использую ThreadPool.QueueUserWorkItem для процедуры, которая использует класс с событиями, то не все события отрабатывают. Как что я делаю неправильно?
    Вот два примера:

    ПЕРВЫЙ:

        Public WithEvents z As clsMyProcess
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim i As Integer
            For i = 0 To 2
                z = New clsMyProcess(i)
                z.ДлительныйПроцесс()
            Next i
        End Sub
    
        Private Sub subSTR(ByVal s As String) Handles z.evntRunning
            Console.WriteLine("Run:" & s)
        End Sub
    
        Private Sub subEND(ByVal s As String) Handles z.evntComplete
            Console.WriteLine("END:" & s)
        End Sub
    
        Private Function фДлительныйПроцесс(ByVal i As Integer) As Boolean
            z = New clsMyProcess(i)
            z.ДлительныйПроцесс()
            Return True
        End Function

    В первом примере (при последовательном вызове процедуры) всё ок. Вывод выглядит так:

    Run:0
    END:0
    Run:1
    END:1
    Run:2
    END:2

    ВТОРОЙ (с использованием ThreadPool.QueueUserWorkItem, всё остальное одинаковое):

        Public WithEvents z As clsMyProcess
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim i As Integer
            For i = 0 To 2
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf фДлительныйПроцесс), i)
            Next i
        End Sub


    В этом случае вывод такой:

    Run:1
    Run:2
    Run:0
    END:0

    Или такой (то есть всегда разный):

    Run:2
    Run:0
    Run:1
    END:2

    КЛАСС:

    Imports System.Threading
    
    Public Class clsMyProcess
    
        Private _Data1 As Integer
    
        Public Event evntComplete(ByVal retData1 As String)
        Public Event evntRunning(ByVal retData1 As String)
    
        Public Sub New(ByRef Data1 As Integer)
            _Data1 = Data1
        End Sub
    
        Public Sub ДлительныйПроцесс()
            RaiseEvent evntRunning(_Data1)
            Dim r As Long
            r = Rnd() * 1000 * _Data1
            Thread.Sleep(r)
            RaiseEvent evntComplete(_Data1)
        End Sub
    End Class


    • Изменено Cross13 23 мая 2014 г. 5:36

Ответы

Все ответы

  • Честно говоря, не разбираюсь в Visual Basic, но предпологаю, что данная запись: z = New clsMyProcess(i) — приводит к отключению обработчиков событий от предыдущего экземпляра, хранившегося в z.
  • Я тоже подозреваю эту строку z = New clsMyProcess(i), но вот как сделать по другому не понимаю. Создать массив z нельзя...

  • Перезаписью z Вы убиваете ссылку на предыдущий объект класса и сборщик мусора удаляет его за ненадобностью. Сделайте z локальной переменной функции потока (т.е. метода фДлительныйПроцесс).

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • На счёт сборщика мусора Вы не правы, так как ссылка на объект сохраняется в параметре this метода clsMyProcess.ДлительныйПроцесс, поэтому сборщик мусора не может его удалить.
  • Вам надо использовать оператор AddHandler для динамического добавления обработчиков к объектам.
    • Помечено в качестве ответа Cross13 24 мая 2014 г. 9:17
  • Спасибо за помощь! С AddHandler всё работает. Сделал вот так:

       Dim cLP As New clsMyProcess(i)
            AddHandler cLP.evntRunning, AddressOf subSTR
            AddHandler cLP.evntComplete, AddressOf subEND
            cLP.ДлительныйПроцесс()
            RemoveHandler cLP.evntRunning, AddressOf subSTR
           RemoveHandler cLP.evntComplete, AddressOf subEND

    Единственное, что смущает, компилятор выдаёт предупреждение: Выражение AddressOf не оказывает влияния в данном контексте, поскольку аргумент метода AddressOf требует гибкого преобразования в тип делегата события. Можно присвоить выражение AddressOf переменной и использовать ее для добавления и удаления метода как обработчика.

    для строк:

    RemoveHandler cLP.evntRunning, AddressOf subSTR
    RemoveHandler cLP.evntComplete, AddressOf subEND

    Но если я использую код из примера - такого сообщения нет:

    Sub TestEvents()
        Dim Obj As New Class1
        ' Associate an event handler with an event.
        AddHandler Obj.Ev_Event, AddressOf EventHandler
        ' Call the method to raise the event.
        Obj.CauseSomeEvent()
        ' Stop handling events.
        RemoveHandler Obj.Ev_Event, AddressOf EventHandler
        ' This event will not be handled.
        Obj.CauseSomeEvent()
    End Sub
    
    Sub EventHandler()
        ' Handle the event.
        MsgBox("EventHandler caught event.")
    End Sub
    
    Public Class Class1
        ' Declare an event.
        Public Event Ev_Event()
        Sub CauseSomeEvent()
            ' Raise an event.
            RaiseEvent Ev_Event()
        End Sub
    End Class
    Я так понимаю, разница в том, что мои события возвращают значение, а в примере нет. Как корректно написать RemoveHandler?


    • Изменено Cross13 26 мая 2014 г. 11:41