[C#/.Net] "Cross-thread operation not valid" debuggen

Pagina: 1
Acties:

Onderwerpen


  • Hardcell
  • Registratie: November 2004
  • Laatst online: 03-02-2023
Ik krijg wel vaker de "Cross-thread operation not valid" melding als ik UI componenten update vanuit een andere thread. Vrij logisch uiteraard. Wat ik me alleen afvraag is hoe ik makkelijk erachter kom welke property / method er wordt aangeroepen die de exception triggered (en die dus eigenlijk ge-invoked moet worden).

Ik probeer momenteel een DateTimePicker vanaf een andere thread te updaten, ik heb hiervoor een thread-safe DateTimePicker gebouwd waar ik de Value en de Text property indien nodig invoke maar dit lijkt niet genoeg.. (Ik update de Value btw vanaf een andere thread)

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 08-09 11:33
Je kunt natuurlijk om elke property die je gebruikt en elke method die je aanroept even "if(control.InvokeRequired()){} doen, maar zo te horen doe je dat al. Krijg je niet een lijn nummer bij je cross thread operation not valid melding?

Heb je trouwens in de DateTimePicker zelf de invokeRequired checks staan of in je thread?

just in case je het net anders deed: http://msdn.microsoft.com...ntrol.invokerequired.aspx

~ Mijn prog blog!


  • Hardcell
  • Registratie: November 2004
  • Laatst online: 03-02-2023
Ik roep alleen de Value aan (d.w.z. ik heb die gedatabind) en die invoke ik al..

  • Hardcell
  • Registratie: November 2004
  • Laatst online: 03-02-2023
Als ik de Text property bind (en ook Invoke) werkt het wel?!

  • ThaStealth
  • Registratie: Oktober 2004
  • Laatst online: 11-09 10:19
Als ik het *goed* heb zijn de .Value en .Text bij een DateTimePicker aan elkaar gekoppeld,

dwz, als jij de. Value update, dan zal er wel een call zijn naar de .Text property die ook geupdate dient te worden. Dit is het probleem wat jij nu merkt.

Als je de .Value via de main thread aanroept heb je het probleem niet, dit kun je doen door een eigen event te scrhijven wat via de Invoke() gecalled wordt.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public delegate void UpdateMeLabel(string _str, Color _fontcolor);
public void FormTxtUpdate(string str, Color fontcolor)
{
if (this.label1.InvokeRequired)
{
 UpdateMeLabel updaterdelegate = new UpdateMeLabel(FormTxtUpdate);
 this.Invoke(updaterdelegate , new object[] { str, fontcolor });
}
else
{
 label1.Text = str;
 label1.ForeColor = fontcolor;
}

} 

(is ff voorbeeldje voor label)

Mess with the best, die like the rest


  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

Heb jij even geluk dat ik dit probleem pas zelf nog gehad heb...

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
''' <summary>
''' Kleine wrapper om een datum te kunnen selecteren. 
''' Omdat de date picker nogal wat bugs bevat en ik geen tijd had 
''' om er zelf een te schrijven was dit de beste oplossing.
''' </summary>
Public Class frmPickDate
    Public Event DatePicked(ByRef SourceTB As TextBox, ByVal Value As DateTime)
    Private _Initial As DateTime
    Private _TB As TextBox
    Private Ready As Boolean = False
    Public Property InitialDate()
        Get
            Return _Initial
        End Get
        Set(ByVal value)
            _Initial = value
            mcMain.SetDate(_Initial)
        End Set
    End Property
    Public Sub TB(ByRef value As TextBox)
        _TB = value
        Ready = True
    End Sub
    Private Sub mcMain_DateChanged(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DateRangeEventArgs) Handles mcMain.DateChanged
        If Ready Then
            Dim Val As DateTime = e.Start
            RaiseEvent DatePicked(_TB, Val)
            'BUGFIX: Als Me.close direct wordt aangesproken krijg je een threading exceptie, om dit op te lossen starten we een thread, en vanuit die thread hoppen we terug naar de UI thread, zodat hij veilig aangeroepen kan worden.
            Dim T As New Threading.Thread(AddressOf Done)
            T.Start()
        End If
    End Sub
    Private Delegate Sub Subs()
    Private Sub Done()
        If Me.InvokeRequired Then
            'BUGFIX: Als Me.Invoke zelfs crashed, dan kan Me.Close() wel worden aangesproken.
            Try
                Me.Invoke(New Subs(AddressOf Done))
            Catch ex As Exception
                Me.Close()
            End Try
        Else
            Me.Close()
        End If
    End Sub
    Private Sub frmPickDate_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not Ready Then Throw New ArgumentException("No Source TextBox Defined!")
    End Sub
    Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
        Done()
    End Sub
End Class

NKCSS - Projects - YouTube


  • whoami
  • Registratie: December 2000
  • Laatst online: 23:16
Zet in je IDE aan dat er moet gebreaked worden van zodra er een exceptie gegooid wordt ?

https://fgheysels.github.io/


  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Hardcell schreef op woensdag 16 september 2009 @ 15:22:
Als ik de Text property bind (en ook Invoke) werkt het wel?!
Dat is opzich ook vrij logisch. Databinding is in principe gewoon een normale event handler. Maar waarom krijg je dan geen cross-thread exception? simpel, jouw object bestaat namelijk in beide threads. Thread 2 veranderd een waarde, maar de UI in thead 1 reageert op het INotifyPropertyChanged event. Hoewel de verandering zelf dus wel cross-thread gebeurt wordt het changed event op 'elke' thread aangeroepen.

Omdat het moment dat jij van in UI een nieuwe thread voorbereid koppel jij een eventhandler aan een event welke in een andere thread wordt geraised. Echter bij databinding koppel bij de eventhandler aan het 'lokale' object. Dat het object zelf in meerdere threads aanwezig is maakt niet uit. Voorwaarde is wel dat het object in de betreffende thread aanwezig blijft. In het uiterste geval kun je het object aan een 'Tag' property van een UI element koppelen, maar meestal zal je hem als (private) field bewaren.

Ik hoop dat ik je vraag heb kunnen beantwoorden.

If it isn't broken, fix it until it is..


  • Mastermind
  • Registratie: Februari 2000
  • Laatst online: 13-09 21:30
Gewoon Control.CheckForIllegalCrossThreadCalls = false; toevoegen in de Program.cs :+

Nee maar even serieus, het staat toch gewoon in de call stack?

  • Hardcell
  • Registratie: November 2004
  • Laatst online: 03-02-2023
ThaStealth schreef op woensdag 16 september 2009 @ 20:43:
Als ik het *goed* heb zijn de .Value en .Text bij een DateTimePicker aan elkaar gekoppeld,

dwz, als jij de. Value update, dan zal er wel een call zijn naar de .Text property die ook geupdate dient te worden. Dit is het probleem wat jij nu merkt.

Als je de .Value via de main thread aanroept heb je het probleem niet, dit kun je doen door een eigen event te scrhijven wat via de Invoke() gecalled wordt.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public delegate void UpdateMeLabel(string _str, Color _fontcolor);
public void FormTxtUpdate(string str, Color fontcolor)
{
if (this.label1.InvokeRequired)
{
 UpdateMeLabel updaterdelegate = new UpdateMeLabel(FormTxtUpdate);
 this.Invoke(updaterdelegate , new object[] { str, fontcolor });
}
else
{
 label1.Text = str;
 label1.ForeColor = fontcolor;
}

} 

(is ff voorbeeldje voor label)
Ik had zelf een thread-safe DateTimePicker gemaakt, die inherit van de standaard DateTimePicker en waarbij ik de Text EN de Value property opnieuw definieer (new keyword) en bij beide setters invoke indien nodig.
Mastermind schreef op donderdag 17 september 2009 @ 09:38:
Gewoon Control.CheckForIllegalCrossThreadCalls = false; toevoegen in de Program.cs :+

Nee maar even serieus, het staat toch gewoon in de call stack?
Nee, dit staat dus niet in de stack dat is het probleem.
Pagina: 1