2014-01-10 2 views
0

Мне нелегко пытаться понять, что кажется случайным броском cross-thread exceptions.Когда ожидать «перекрестного потока»?

Примеры

При вызове в другом потоке, почему это работает:

Dim text As String = Me.Text 

В то время как это будет сгенерировано исключение:

Me.Text = "str" 

Что делает его еще более странным является что следующие do работают:

Dim text As String = Me.ctl.Margin.ToString() : Me.ctl.Margin = New Padding(1, 2, 3, 4) 
Dim text As String = Me.ctl.MyProp : Me.MyProp = "str" 

Примечание

Да, я знаю, что я мог бы просто вызвать свойство как это:

Me.Invoke(Sub() Me.Text = "str") 

Вопрос

Так что, когда я могу ожидать cross-thread exception?

Код

Это код, который я использовал для тестирования Me.Text свойства:

Public Class Form1 

    Public Sub New() 
     Me.InitializeComponent() 
     Me.ctl = New Control() 
     Me.ctl.Text = "test_control" 
     Me.Controls.Add(Me.ctl) 
    End Sub 

    Private Sub TestGet(sender As Object, e As EventArgs) Handles Button1.Click 
     Dim t As New Thread(AddressOf Me._Proc) 
     t.Start(TESTTYPE.GET) 
    End Sub 

    Private Sub TestSet(sender As Object, e As EventArgs) Handles Button2.Click 
     Dim t As New Thread(AddressOf Me._Proc) 
     t.Start(TESTTYPE.SET) 
    End Sub 

    Private Sub _Proc(tt As TESTTYPE) 
     Dim text As String = String.Empty 
     Dim [error] As Exception = Nothing 
     Try 
      If (tt = TESTTYPE.GET) Then 
       text = Me.ctl.Text 
      ElseIf (tt = TESTTYPE.SET) Then 
       Me.ctl.Text = "test" 
      End If 
     Catch ex As Exception 
      [error] = ex 
     End Try 
     Me.Invoke(Sub() Me._Completed(tt, text, [error])) 
    End Sub 

    Private Sub _Completed(tt As TESTTYPE, text As String, ByVal [error] As Exception) 
     If ([error] Is Nothing) Then 
      If (tt = TESTTYPE.GET) Then 
       MessageBox.Show(String.Concat("Success: '", text, "'"), tt.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Information) 
      ElseIf (tt = TESTTYPE.SET) Then 
       MessageBox.Show("Success", tt.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Information) 
      End If 
     Else 
      MessageBox.Show([error].Message, tt.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error) 
     End If 
    End Sub 

    Private ReadOnly ctl As Control 

    Private Enum TESTTYPE 
     [GET] = 0 
     [SET] = 1 
    End Enum 

End Class 

Редактировать

Это не бросить исключение:

Public Event TestChanged As EventHandler 

Public Property Test() As String 
    Get 
     Return Me.m_test 
    End Get 
    Set(value As String) 
     If (value <> Me.m_test) Then 
      Me.m_test = value 
      Me.Invalidate() 
      RaiseEvent TestChanged(Me, EventArgs.Empty) 
     End If 
    End Set 
End Property 

ответ

0

Хорошо, поэтому я сделал некоторые исследования самостоятельно, и выясняется, что исключение возникает из .Handle имущество Control.

<Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DispId(-515), SRDescription("ControlHandleDescr")> _ 
Public ReadOnly Property Handle As IntPtr 
    Get 
     If ((Control.checkForIllegalCrossThreadCalls AndAlso Not Control.inCrossThreadSafeCall) AndAlso Me.InvokeRequired) Then 
      Throw New InvalidOperationException(SR.GetString("IllegalCrossThreadCall", New Object() {Me.Name})) 
     End If 
     If Not Me.IsHandleCreated Then 
      Me.CreateHandle() 
     End If 
     Return Me.HandleInternal 
    End Get 
End Property 

System.Windows.Forms.resources:

IllegalCrossThreadCall = Кросс-нить операция не действует: Control '{0}' доступ из потока, отличного от резьбы она была создана ,

Так что я считаю, что ответ будет что-то вроде этого: (. Я дам вам все до голосования, как все ответы актуальны по отношению к поперечной резьбе)

Whenever the handle of a control is invoked from a different thread.

следующий код будет сгенерировано исключение:

Public Class Form1 

    Private Sub Test(sender As Object, e As EventArgs) Handles Button1.Click 
     Dim t As New Thread(AddressOf Me._Proc) 
     t.Start() 
    End Sub 

    Private Sub _Proc(id As Integer) 
     Dim [error] As Exception = Nothing 
     Try 
      Dim p As IntPtr = Me.Handle 
     Catch ex As Exception 
      [error] = ex 
     End Try 
     Me.Invoke(Sub() Me._Completed([error])) 
    End Sub 

    Private Sub _Completed(ByVal [error] As Exception) 
     If ([error] Is Nothing) Then 
      MessageBox.Show("Success", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information) 
     Else 
      MessageBox.Show([error].Message, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) 
      Me.tbDescription.Text = [error].Message 
     End If 
    End Sub 

End Class 
1

Итак, когда можно ожидать исключения для перекрестных потоков?

Очень просто, когда вы доступ к некоторым function или property элемента управления, из потока, которые не имеют права доступа к нему.

Например, в приложении Window form, когда вы пытаетесь получить доступ к кнопке, помещенной в форму из потока, отличного от ui, то есть не основного потока (и вы не установили флаги вручную, чтобы разрешить операцию поперечного потока)

EDIT согласно комментарию, how can I know I can/can not access a getter/ setter of a property. Where are the access rights defined? вы всегда можете быть на безопасной стороне, запрашивая InvokeRequired свойство элемента управления в Windows,

+0

Что вы подразумеваете под «правами на доступ»? Как вы можете видеть в примере, я _can_ обращаюсь к методу 'get_Text' элемента управления, но' set_Text' выдает исключение. –

+0

«права на доступ» означает, что если я пытаюсь получить доступ к 'функции' или' property' для элемента управления, из 'thread', который не имеет права его вызывать ....... – dbw

+0

Вы имеете в виду [доступ уровни] (http://msdn.microsoft.com/en-us/library/76453kax.aspx)? Если да, это довольно очевидно. Если нет, то как я могу узнать, могу ли я/не получить доступ к getter/setter свойства. Где определяются права доступа? –

2

основное время исключение кросс-нить происходит, когда вы делаете что-то, что вызвало бы событие огонь из потока, отличного от UI, который влияет на поток пользовательского интерфейса; Поэтому чтение свойства может быть прекрасным, но запись свойства элемента управления приведет к его перерисовке (по крайней мере), следовательно, к исключению.

Конечно, другие производители, возможно, использовали исключения для других сценариев, где это не безопасно для доступа из другого потока

+0

Это может быть правдой для некоторых собственных свойств/методов, но проверьте мое редактирование. –

+0

Исключение кросс-потока возникает, когда поток пользовательского интерфейса обнаруживает его - может быть, Invalidate делает Invoke для вас; и ваше событие не касается пользовательского интерфейса. –

+0

Я знаю. Это фиктивный код, который не предназначен для использования. Это просто для исследования. Я сам написал ответ. Если у вас есть какие-то мысли, не стесняйтесь оставлять комментарии. –

1

Так что, когда я могу ожидать исключение кросс-нить?

Ну, графический интерфейс в .Net создан в STA, что означает, что только потоки, которые создают элемент управления, могут обновлять его, это связано с концепцией Thread-safe. по этим причинам, когда вы запускаете другой поток и пытаетесь получить доступ к элементу управления, который принадлежит основному потоку, вы получите invalidOperationException

+0

Тогда почему я могу вызвать 'Me.Invalidate()' из другого потока, кроме 'Me' и все еще не получить исключения? –

+0

Me ссылается на текущий экземпляр, подобный этому, в C# - это не другой поток –

Смежные вопросы