Autor Beitrag
derkollo
Hält's aus hier
Beiträge: 7



BeitragVerfasst: So 04.04.10 13:49 
Frohe Ostern! ;)

...ich bin noch ein relativer Neuling was C# betrifft. Zur Zeit weiß ich einfach nicht weiter.

Problematik:

Ich habe eine Klasse Namens ExtendendMidiClock geschrieben. In dieser Klasse ist ein Timer implementiert, dem in meiner Klasse erweiterte Funktionen zugewiesen werden. Eine Funktion ist die Teilung des Timer.Tick. Zur Veranschaulichung: Ich möchte z.B. bei jedem 10. Tick ein extra Event auslösen.

So weit so gut. Die Logik steht. Nun habe ich die ExtendedMidiClock in mein Form1 implementiert und wollte z.B. bei jedem 10er Tick Event eine Methode auslösen.
Also eigentlich nichts anderes als wenn ich einen Timer implementiere, und dem Timer.Tick Event eine Methode zuweise.

Doch ich bekomme eine Fehlermeldung. Stichwort Multithreading.

Aber warum? Warum kann ich Methoden direkt an einen Timer hängen, aber nicht an meine ExtendedMidiClock?

Ich weiß leider auch nich so genau, wonach ich suchen muss. BackgroundWorker und Multithreading brachten mich nicht weiter. Denn ich möchte ja keinen neuen Thread öffnen....
Und wenn ich eine Methode an einen Timer.Tick hänge, brauche ich auch keinen Delegaten...

Was übersehe ich hier? Habe ich beim Event einen Fehler gemacht?
Was muss ich beachten, um Events zu generieren, die von der instanzierenden Klasse genutzt werden können?


Danke im Vorraus und schöne Feiertage euch noch!


Grüße,

derkollo
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: So 04.04.10 13:58 
user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Frohe Ostern! ;)

Ebenso!

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Doch ich bekomme eine Fehlermeldung. Stichwort Multithreading.

Ohne genauere Beschreibung (mit Code und Fehlermeldung) wäre alles nur Raten.

Jürgen
derkollo Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: So 04.04.10 15:56 
Hallo und danke für die schnelle Antwort.

Natürlich könnte ich dir den ganzen Code schicken, wäre nur ein wenig zu lang das ganze...

Letztendlich geht es ja nur darum, ein Eventereigniss zu erstellen, welches ohne Mudltithreading auskommt...


Hier die Logik.

[Klasse ExtendedClock]

Timer tm = new timer;

event ticker;

tm.tick += Eigene Methode

void Eigene Methode()
{
...
if (ticker!= null) ticker();
}


[Klasse B]

ExtendedClock EClock = new ExtendedClock;

EClock .ticker += MethodeKlasseB;

void MethodeKlasseB()
{
textbox1.Text = "xyz";
}


Wenn ich nun das Programm starte, kommt die Fehlermedlung(ähm, Bezeichner wie public habe ich natürlich nur in dem Beispiel weggelassen. Zur Probe steht im Moment alles auf public....):

Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBox1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.



Ich dachte, genau dafür sind Ereignisse da. Um Threadunabhänige Ereigniss bereitzustellen....


Wie gesagt, wenn ich z.B: die "MethodeKlasseB" an einen "normalen" Timer.Tick hänge. Beschwert er sich ja auch nicht...

Wo liegt hier mein Denkfehler?


Grüße,

Mathias
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: So 04.04.10 16:31 
Erstens: Mache deinen Code lesbar; dafür gibt es die Formatierung. Gehe auf deinen Beitrag mit dem Schere-Button, "Bereiche" öffnen, in der ComboBox C# wählen, überflüssige Leerzeilen entfernen, Code-Bereich markieren, Plus-Button, dann den Bereich der zweiten Klasse markieren und Plus-Button, "Vorschau" benutzen und ggf. korrigieren, erst dann "Absenden".

Zweitens: Es gibt drei verschiedene Timer mit unterschiedlichem Zweck. Zu Windows-Forms gehört üblicherweise System.Windows.Forms.Timer, welchen benutzt du?

Drittens:
Zitat:
Ich dachte, genau dafür sind Ereignisse da. Um Threadunabhänige Ereigniss bereitzustellen....

Nein, Ereignisse sind dazu da, dass nicht der Programmierer steuert, wann etwas geschieht, sondern der Anwender. Das hat überhaupt nichts mit Threads bzw. Thread-Unabhängigkeit zu tun.

Wozu brauchst du überhaupt mehrere Threads? Bei einer Windows-Anwendung dienen die eigentlich dazu, dass langdauernde Arbeiten "im Hintergrund" erledigt werden können und "im Vordergrund", d.h. in der GUI weitergearbeitet werden kann. Ich kann aus deiner Beschreibung nichts dergleichen erkennen.

Gruß Jürgen
derkollo Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: So 04.04.10 17:24 
Danke für die Beschreibung zum posten von Code. Muss ich gleich mal testen.
Aber ich meinte mit Länge des Codes, die mehrfache Verschachtelung von Klassen...

Ich habe mal das Wesentliche herauskopiert:

ausblenden volle Höhe C#-Quelltext
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:
namespace StepSequencer.MIDI.Classes
{
    public class ExtendedMidiClock
    {
        public ExtendedMidiClock()
        {
            MidiClock.Tick += new EventHandler(MidiClock_Tick);
        }

        public MidiInternalClock MidiClock = new MidiInternalClock();

        public void MidiClock_Tick(object sender, EventArgs e)
        {
            #region Generate Pattern Tick

            switch (patternChanged)
            {
                case true:
                    if (Tick_Pattern != null)
                    {
                        Tick_Pattern(this, e);
                    }
                    patternChanged = false;
                    break;
                case false:
                    break;
            }

            #endregion

           ....

            overallTickCounter++;


            switch ((overallTickCounter % ticksPerStep) == 0)
            { 
                .... (eigentliche Logik)
                patternChanged = true;
            }
        }

      ....

        public event EventHandler<EventArgs> Tick_Pattern;

....
    }
}


Das ist die Klasse des Timers. Grundlage ist der "MidiInternalClock" Timer von Leslie Sanford. Dieser basiert auf einem System.Timer. (An dieser Stelle nochmal ein dickes Danke an Leslie, für das großartige C# Midi-Toolkit!!)

Und nun als Testform:

ausblenden volle Höhe C#-Quelltext
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:
namespace StepSequencer
{
    public partial class Sequencer : Form
    {
        MIDI.Classes.ExtendedMidiClock XMClock = new StepSequencer.MIDI.Classes.ExtendedMidiClock();

        public Sequencer()
        {
            InitializeComponent();

            XMClock.Tick_Pattern += new EventHandler(ExtndMidiClock_Tick_Pattern);

        }

        public void ExtndMidiClock_Tick_Pattern(object sender, EventArgs e)
        {
            textBox1.Text = "asda";
        }

        private void button1_MouseDown(object sender, MouseEventArgs e)
        {
            XMClock.Start();
        }

        private void button1_MouseUp(object sender, MouseEventArgs e)
        {
            XMClock.Stop();
        }
        
    }
}


Letztendlich soll der Timer eine "MIDI-Clock" darstellen. Also ein Timer, der zur Synchronisierung von "Musikereignissen" benötigt wird.

Zitat:

Nein, Ereignisse sind dazu da, dass nicht der Programmierer steuert, wann etwas geschieht, sondern der Anwender. Das hat überhaupt nichts mit Threads bzw. Thread-Unabhängigkeit zu tun.


Ja, nicht direkt. Aber ich dachte, dass das Benutzen von Events, ein Multithreading "unnötig" macht.
Im Sinne von: "Nutze Events, und du brauchst dich nicht mit Multithreading auseinandersetzen..." ;)
Zumindest noch nicht hier. Da es sich um eine Musiksoftware handelt, überlege ich, jegliche GUI Methoden per Multithreading zu realisieren... Während sich die GUI aktualisiert, darf der eigentliche Thread nicht unterbrochen werden. Aber alles zur GUI kommt später...

Damit dürfte auch deine nächste Frage beantwortet sein. Eigentlich möchte ich zur Zeit nur einen Thread "bedienen".
Der von mir geschriebene Timer soll instanziert werden können, und dem Hauptthread dann zur Verfügung stehen. Also auch in ihm abgearbeitet werden.
Eigentlich ist es mir sogar wichtig, das die verschiedenen Tickereignisse meines Timers nacheinander erledigt werden!
In dem Code oben, habe ich nur ein Event. Die anderen sind identisch aufgebaut, werden nur zu verschiedenen Zeiten gestartet.
Ein Multithreading an dieser Stelle, würde das gesamte Programm instabil werden lassen.

Zur Zeit verstehe ich ja nicht, warum mein Timer in einem eigenen Thread behandelt wird. Ich rufe, zumindest bewusst, an keiner Stelle einen weiteren Thread auf.
Oder wird durch ein Event, automatisch ein neuer Thread gestartet?

Ich merke schon, ich muss mich unbedingt mal genauer durch das Thema Threading einarbeiten...


Ich hoffe, dass Problem ist nun ein wenig anschaulicher. :)

Vielleicht noch zur Ergänzung:

In dem Beispiel habe ich die Methoden zum Verändern der Textbox in Form1 eingebaut.
Letztendlich wird der Timer aber in Klassen eingebaut, welche wiederum instanziert werden. Zum Beispiel soll es 5 Klassen geben, welche jede unabhängigen Timer hat. Diese Klassen können dann gerne in jeweils einzelnen Threads abgearbeitet werden. Doch inwiefern das Sinn macht, kann ich noch nicht sagen...
Die Timer müssen aber jeweils im Thread der instanzierenden Klasse abgearbeitet werden!

Ich hoffe, du verstehst was ich meine. ;)

Nochmal danke für deine Bemühungen an einem Ostersonntag!!
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: So 04.04.10 18:09 
user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Aber ich meinte mit Länge des Codes, die mehrfache Verschachtelung von Klassen...

Das war mir schon klar, aber mit dem, was du vorher unformatiert gebracht hattest, konnte ich nichts anfangen.

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Das ist die Klasse des Timers. Grundlage ist der "MidiInternalClock" Timer von Leslie Sanford. Dieser basiert auf einem System.Timer.

Ah ja. Damit muss ich mich endgültig verabschieden mangels Detailkenntnissen.

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Ja, nicht direkt. Aber ich dachte, dass das Benutzen von Events, ein Multithreading "unnötig" macht.
Im Sinne von: "Nutze Events, und du brauchst dich nicht mit Multithreading auseinandersetzen..." ;)

So eine Formulierung kann ich überhaupt nicht verstehen. Woher hast du das denn? :shock:

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Da es sich um eine Musiksoftware handelt, überlege ich, jegliche GUI Methoden per Multithreading zu realisieren... Während sich die GUI aktualisiert, darf der eigentliche Thread nicht unterbrochen werden. Aber alles zur GUI kommt später...

Zur Zeit verstehe ich ja nicht, warum mein Timer in einem eigenen Thread behandelt wird. Ich rufe, zumindest bewusst, an keiner Stelle einen weiteren Thread auf.

Ich merke schon, ich muss mich unbedingt mal genauer durch das Thema Threading einarbeiten...

Das scheint in diesem Zusammenhang äußerst sinnvoll zu sein.

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Oder wird durch ein Event, automatisch ein neuer Thread gestartet?

Grundsätzlich keinesfalls, und auch in dem bisher vorgestellten Code ist nichts davon zu lesen. Ein neuer Thread muss (wie jedes Objekt) ausdrücklich erzeugt und ggf. aktiviert werden.

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Ich hoffe, dass Problem ist nun ein wenig anschaulicher. :)

So ist es. Deshalb muss ich mich jetzt auch verabschieden.

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Vielleicht noch zur Ergänzung:

In dem Beispiel habe ich die Methoden zum Verändern der Textbox in Form1 eingebaut.
Letztendlich wird der Timer aber in Klassen eingebaut...

Deshalb noch diese Ergänzung: Alles, was zur GUI gehört, muss immer im selben (Primär-) Thread ablaufen und kann nicht ausgelagert werden. "Hintergrund"-Threads können nicht direkt auf die GUI zugreifen (das sagte oben die Fehlermeldung, und das macht die Zusammenarbeit weiterer Threads mit der GUI etwas umständlich).

Viel Erfolg! Jürgen
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: So 04.04.10 18:42 
Ich weiß zwar nicht, warum dieser Sanford seinen eigenen Timer schreibt, aber der Nebenthread wird jedenfalls durch timeSetEvent verursacht.

user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich ist es mir sogar wichtig, das die verschiedenen Tickereignisse meines Timers nacheinander erledigt werden!
Da timeSetEvent anscheinend exakt einen Nebenthread benutzt, können die Ereignisse sich nicht selbst überholen. Auch nicht, nachdem du sie per (Begin)Invoke an den Hauptthread geschickt hast.

user profile iconJüTho hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconderkollo hat folgendes geschrieben Zum zitierten Posting springen:
Ja, nicht direkt. Aber ich dachte, dass das Benutzen von Events, ein Multithreading "unnötig" macht.
Im Sinne von: "Nutze Events, und du brauchst dich nicht mit Multithreading auseinandersetzen..." ;)

So eine Formulierung kann ich überhaupt nicht verstehen. Woher hast du das denn? :shock:
In sehr engem Rahmen kann man das so stehen lassen ;) . Oft gibt es als Alternative zu einem konstanten Polling oder blockenden Aufruf, die dann asynchron zum GUI-Thread ausgeführt werden müssten, die Möglichkeit, einen synchronen Callback zu registrieren, der dann meistens über Window Messages aktiviert wird (oder das Framework den asynchronen Callback durch SynchronizationContext versteckt). Ob es so etwas gibt, hängt aber ganz vom Anwendungsfall ab; Thread.Sleep<->Forms.Timer wäre so ein Paar.
Spätestens bei Parallel Programming kommt man aber an Threads nicht mehr vorbei ;) .

_________________
>λ=
derkollo Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: So 04.04.10 20:12 
Hmm... so langsam kommen wir der Sache näher. Auch wenn JüTho jetzt resignieren muss. ;) Trotzdem bis hier hin danke!

So Kha,

Zitat:
Ich weiß zwar nicht, warum dieser Sanford seinen eigenen Timer schreibt, aber der Nebenthread wird jedenfalls durch timeSetEvent verursacht.


Eine MIDI-Clock ist nunmal etwas komplexer als ein simpler Timer. Mein Timer wird ja auch wesentlich komplexer... ;)
Das timeSetEvent habe ich mir direkt mal angeschaut. Macht ja alles soweit Sinn. Jedem Timer wird sein eigener Thread zugeordnet.
Doch das beantwortet nicht die Frage, warum ich einen Timer instanzieren kann, und eine Methode direkt an ein Tick.Event hängen kann, während ich das bei meinem Timer nicht machen kann...
Muss ich meine Klasse als abstract oder struct definieren? Oder muss ich das Event über einen Callback aufrufen um so den Haupthread vom Timerthread zu "lösen"?

Grüße
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Mo 05.04.10 11:42 
Bei WPF siehe Dispatcher (CheckAccess/BeginInvoke/Invoke) und bei WinForms siehe InvokeRequired/BeginInvoke/Invoke. (BeginInvoke ist asynchron, Invoke ist synchron)
derkollo Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Mo 05.04.10 15:53 
Sooo,

nachdem ich mir fast die ganze Nacht um die Ohren gehauen habe, bin ich endlich auf die absolut passende Antwort gestoßen.

Jegliche GUI-Zugriffe bzw. Änderungen geschehen über ein extra "GUI-Thread". Aus diesem Grund, kann ich zwar Methoden von Events starten lassen, doch nur wenn keine GUI-Elemente bzw. Controls manipuliert werden sollen!

Möchte ich auch auf ein anderes Thread, hier z.B. das GUI-Thread, zugreifen, muss ich mit Delegaten und invoke arbeiten.

Letztendlich habe ich zu diesem Zweck eine geniale Lösung gefunden:

dotnet-snippets.de/d...schaften-SID829.aspx

Mit dieser Kombination lassen sich beliebige Control-Elemente invoken. Sehr sehr praktisch!


Danke nochmal für eure Hilfe und einen schönen Ostermontag euch noch!


Grüße,


derkollo