Autor Beitrag
Csharp-programmierer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Do 20.10.16 20:15 
Hallo Community,
ich habe ein Problem mit der Effizient zweier Algorithmen.

Auf einem SQL Server werden alle Nachrichten die man schreibt gespeichert. Wird eine Nachricht versendet, werden 2 Datensätze für die gleiche Nachricht eingetragen, aber mit unterschiedlichem Status. Das sorgt dafür, dass die zwei Benutzer die Nachrichten getrennt voneinander editieren können. Die C# Anwendung soll jetzt in einem gewissen Zeitraum Anfragen an den Server raushauen und dann schauen, ob es eine neue Nachricht gibt. Bis jetzt funktioniert es schon: ich habe einen Timer auf die Form gezogen, diesen aktiviert und ein Intervall festgelegt. In dem Tick-Event wird dann geschaut, ob es eine neue Nachricht gibt. Das funktioniert zwar, aber zumal dieser Code in einer DLL läuft und diese DLL in einer .exe eingebunden wird, muss auch die .exe ständig nach neuen Nachrichten suchen.

Meine Vorüberlegung:
Ein Prozessor hat ja verschieden viele Kerne. Kann man es eventuell so programmieren, dass Kern 1 beispielsweise die .exe ausführt und Kern 2 permanent schaut, ob eine neue Nachricht vorhanden ist? Ich habe mal gehört, dass ein Prozessor mit 4 Kernen beispielsweise 4 Rechenoperationen parallel ausführen kann. Das würde ja dann die Performance des Programms verbessern, oder?

Wie kann man dieses Performance Problem am besten lösen?


Moderiert von user profile iconChristian S.: Topic aus Algorithmen, Optimierung und Assembler verschoben am Fr 21.10.2016 um 19:47

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20014
Erhaltene Danke: 1843

Win 10
C# (VS 2015)
BeitragVerfasst: Do 20.10.16 20:35 
Du kannst einen BackgroundWorker benutzen, um die Abfragen im Hintergrund auszuführen. Das Betriebssystem entscheidet dann selber, auf welchen Kern das muss.

Aber wieso ist das Ganze überhaupt ein Problem der Rechenleistung? Rufst Du die Datenbank in so kurzen Intervallen ab? Oder blockiert einfach nur der GUI-Thread, sodass es den Anschein hat, dass es ein Performanceproblem ist?

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Do 20.10.16 21:02 
Naja. Ich hatte den Timer erst auf 5 Sekunden. Wenn man dann beispielsweise irgendwelche Daten in TextBoxen eingegeben hat, hat man wirklich leichte Verzögerungen gemerkt. Für meinen I7 mit 4 Gh Taktfrequenz sollte das eigentlich kein Problem sein (war es auch nicht wirklich) aber mein Laptop hatte da schon leichte Schwierigkeiten. Und wenn ich dan mal daran denke, was unsere "Kunden" eventuell für langsame Rechner haben, hatte ich schon Bauchschmerzen. Naja, dann werde ich ´mich mal über den BackgroundWorker informieren :)

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20014
Erhaltene Danke: 1843

Win 10
C# (VS 2015)
BeitragVerfasst: Do 20.10.16 21:11 
Leichte Verzögerungen sagt ja erst einmal nichts darüber aus, ob da ein Kern ausgelastet ist oder nicht. Es kann auch sein, dass Du den UI-Thread mit der Anfrage an der SQL-Server blockierst, sodass er keine Eingaben verarbeitet.

Pack Dir mal einen Button auf eine Form und rufe in dessen Klick-Ereignis System.Threading.Thread.Sleep(10000); auf. Dann versuche in den nächsten zehn Sekunden, irgendwas mit Deinem Programm anzustellen. Du wirst sehen, dass es nicht reagiert, obwohl der Prozessor genau gar nichts zu tun hat. Genau solche Blockaden des UI-Threads können Dir eventuell auch bei der Abfrage des Servers passieren.

Ein Background-Worker sollte aber dafür sorgen, dass das nicht mehr passiert, weil die Abfragen nicht mehr im UI-Thread stattfinden.

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 842
Erhaltene Danke: 135

Win7
VS 2013, VS2015
BeitragVerfasst: Do 20.10.16 21:32 
Falls du ein aktuelles C# verwendest, kannst du ja await benutzen. Dann könnte das bspw. so aussehen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
        private async void button_Click(object sender, RoutedEventArgs e)
        {
            var t = Task.Run(() =>
            {
                // SQL Abfrage, was auch immer
                return DoProcessing();
            });
            await t;
            MessageBox.Show("Ergebnis: " + t.Result);
        }

        private int DoProcessing()
        {
            Thread.Sleep(1000);
            return 42;
        }


Durch das await wird gewartet, ohne das Fenster zu blockieren. Der UI-Thread wird also frei, während die Arbeit in dem Task auf einem anderen Kern ausgeführt wird.
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Do 20.10.16 21:34 
Ah okay. Ich habe den BackgroundWorker jetzt mal ausprobiert und es funktioniert :)

Jetzt aber zu dem Server... Wenn - sagen wir mal - 100 Benutzer PC's gleichzeitig online sind und mit dem Netzwerk verbunden sind, alle PCs senden ununterbrochen Anfragen an den Server. Wird der dann nicht irgendwann maßlos überlastet werden?

@jheins
So ganz genau verstehe ich den Code noch nicht. Was heißt zum Beispiel async und await?

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 842
Erhaltene Danke: 135

Win7
VS 2013, VS2015
BeitragVerfasst: Fr 21.10.16 08:19 
Mein Code macht im Grunde das gleiche, allerdings dürfte er etwas kürzer sein (keine Ahnung, wie viel Code in deinen BackgroundWorker steckt) :)

Von der Bedeutung her ist await ein nicht blockierendes warten auf das Ergebnis eines Task-Objekts. Eine Methode, die await enthält, muss mit dem Schlüsselwort async gekennzeichnet sein.
Ein Task<int> ist ein Vorgang, der eventuell noch läuft, der schlussendlich einen int liefern wird. mit await t wird darauf gewartet, dass der Task auch wirklich beendet ist.

Weiterführende Literatur:
openbook.rheinwerk-v...012/1997_15_004.html (siehe auch das vorgehende Kapitel)
www.heise.de/develop...d-await-1852797.html
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Fr 21.10.16 15:52 
Ah okay. Nun habe ich deinen Code mal übernommen.

Jetzt gibt es ein kleines Problemchen. Sagen wir mal angenommen alle 5 Sekunden wird eine Serveranfrage ausgesendet, alle ungelesenen Nachrichten in der Datenbank werde ausgegeben. Wenn beispielsweise eine neue Nachricht gefunden wird, dann soll im rechten oberen Bildschirmbereich ein kleines Fenster angezeigt werden. Nach 5 Sekunden kommt das Fenster dann wieder. Wenn man dann 2 ungelesene Nachrichten hat, wird Nachricht 1 angezeigt, obwohl der User darüber schon informiert wurde...

Wie kann man dieses Problem noch lösen? In deinem Code braucht man ja eine Methode, die einen Wert returnd. Dann eventuell einen String?

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1452
Erhaltene Danke: 251

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 21.10.16 19:04 
Das Problemchen richtet sich mehr nach einem Konzept statt nach einer Optimierung. Richte dafür ein eigenes Thema ein.

_________________
„Wo andere blind der Wahrheit folgen, denk daran ... Nichts ist wahr!" (Assassin's Creed I-II)
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Fr 21.10.16 19:42 
Okay.
Wenn ich jetzt die async Methode aufrufe, kommt dieser Fehler:

Ein Ausnahmefehler des Typs "System.Reflection.TargetInvocationException" ist in mscorlib.dll aufgetreten.
Zusätzliche Informationen: Ein Aufrufziel hat einen Ausnahmefehler verursacht.

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:
private async void CheckForNews()
        {
            var t = Task.Run(() =>
            {

                MessageBox.Show("Test");
                int i = 0;
                string URL = "Die URL";
                WebClient webClient = new WebClient();
                NameValueCollection formData = new NameValueCollection();
                formData["username"] = Benutzername;
                formData["action"] = "ungelesen";

                byte[] responseBytes = webClient.UploadValues(URL, "POST", formData);
                string responsefromserver = Encoding.UTF8.GetString(responseBytes);
                webClient.Dispose();
                string[] AlleNachrichten = responsefromserver.Split('|');
                foreach (string allenachrichten in AlleNachrichten)
                    i++;

                i--;
                if (i > MaxNewMessage)
                {
                    MaxNewMessage = i;
                    this.button1.Text = "meine Nachrichten  [" + MaxNewMessage.ToString() + "]";
                }
                else if (i == MaxNewMessage)
                    this.button1.Text = "meine Nachrichten  [" + MaxNewMessage.ToString() + "]";
                else
                    this.button1.Text = "meine Nachrichten";
            });
            await t;

            System.Threading.Thread.Sleep(5000);
        }


Ich rufe diese Methode einmalig in dem Konstruktor auf. Oder wo muss ich sie sonst aufrufen?

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3456
Erhaltene Danke: 666

Win7
C++, C# (VS 2010/12/13/15)
BeitragVerfasst: Fr 21.10.16 19:56 
Bei TargetInvocationException immer in der InnerException nachschauen, was die eigentliche Ursache ist (generell ist es gut, dort bei einer Exception hereinzuschauen, nur bei anderen Exceptions ist diese meistens einfach null).

Edit: Ich tippe (bzw. sehe es schon), daß dies ein "ungültiger threadübergreifender Zugriff" ist (UI-Elemente darfst du nur im UI-Thread ansprechen) - Stichwort: Invoke.

Sofern die Methode CheckForNews nur vom UI-Thread aufgerufen wird, kannst du auch einfach den UI-Code nach dem await ausführen.
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20014
Erhaltene Danke: 1843

Win 10
C# (VS 2015)
BeitragVerfasst: Fr 21.10.16 21:51 
Das explizite Verpacken in den Task brauchst Du nicht wirklich. Es reicht, wenn Du den Aufruf an den WebClient asynchron machst. Könnte dann irgendwie so aussehen:

ausblenden 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:
        private async Task<int> GetMessageCount()
        {
            NameValueCollection formData = new NameValueCollection();
            formData["username"] = Benutzername;
            formData["action"] = "ungelesen";

            byte[] responseBytes;
            using (var wc = new WebClient())
                responseBytes = await wc.UploadValuesTaskAsync("http://meineadresse.de", WebRequestMethods.Http.Post, formData);

            var response = Encoding.UTF8.GetString(responseBytes);
            var allMessages = response.Split('|');

            return allMessages.Length;
        }

        private async Task CheckForNewMessages()
        {
            var messageCount = await GetMessageCount();
            button1.Text = messageCount >= maxMessageCount ? $"meine Nachrichten [{messageCount}]" : "meine Nachrichten";
            if (messageCount > maxMessageCount)
                maxMessageCount = messageCount;
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            await CheckForNewMessages();
        }

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".