Autor Beitrag
m.keller
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 133

Win xp, Win 7
C# (VS 2008)
BeitragVerfasst: Mo 08.02.16 09:25 
Guten Morgen allerseits,

vermutlich ist meine Frage einfach zu lösen, allerdings befinden sich gerade jede menge Knoten in meinem Kopf und ich sehe den Baum vor lauter Bäumen nicht mehr. ;)


Nach mehreren versuchen vermute ich bei meiner Software ein Speicherproblem.
Diese ist auf einem C# 32 bit Projekt aufgesetzt.
Ich starte alle 10 Sekunden einen Thread (Background) der selbstständig sich beendet.
Es wird sichergestellt, das dieser nur einmal existiert.

Nun stürzt die Software immer nach der gleichen anzahl von Threadstars ab.
Vermutlich da im Thread größerer Arrays erstellt und nicht freigeben werden.
Irgendwie beleiben die Daten scheinbar vorhanden.
Ach so, die einzelnen Daten werden als event aus dem Thread versendet.

Meine Frage, wie kann ich den Thread vom GC freigeben lassen.

_________________
Der gesunde Menschenverstand ist nur eine Anhäufung von Vorurteilen, die man bis zum 18. Lebensjahr erworben hat. (Albert Einstein)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 08.02.16 10:42 
Hallo,

warum erstellst du dann immer wieder einen neuen Thread, wenn du den jede 10 Sekunden wieder neustartest?
Besser wäre es sowieso statt eigener Threads einfach Tasks zu benutzen, da diese selbständig aus dem ThreadPool entsprechende Threads benutzen.
m.keller Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 133

Win xp, Win 7
C# (VS 2008)
BeitragVerfasst: Mo 08.02.16 11:03 
Danke erst einmal,
ich werde es mal mit Task versuchen.
Ich muss ihn immer wieder neu Starten, weil ich nicht vorhersehen kann ob es mal 10 sec oder mal 60 sec. dauern wird.

_________________
Der gesunde Menschenverstand ist nur eine Anhäufung von Vorurteilen, die man bis zum 18. Lebensjahr erworben hat. (Albert Einstein)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 08.02.16 12:35 
Zitat:
Ich muss ihn immer wieder neu Starten, weil ich nicht vorhersehen kann ob es mal 10 sec oder mal 60 sec. dauern wird.

In Kombination mit der Aussage
Zitat:
Ich starte alle 10 Sekunden einen Thread (Background) der selbstständig sich beendet.
Es wird sichergestellt, das dieser nur einmal existiert.

Du startest alle 10 sec die Aufgabe die schonmal 'ne Minute dauern kann garantierst aber das es denn Thread nur einmal gibt :gruebel:
Das klingt kaput egal über welches Threadingverfahren du das implementierst.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 08.02.16 13:10 
Du kannst prüfen ob ein Thread noch läuft.
Dann musst Du dich auch nicht darum kümmern, dass nur ein Thread existiert.

Einfach alle 10 Sekunden schauen, ob das Ding noch läuft.
Läuft er nicht, starte neu, ohne den Thread neu zu erstellen, sondern einfach neu starten.
Läuft er noch, warte auf die Beendigung und starte dann neu, sobald er beendet ist.

Da es kein StateChanged-Event gibt (zumindest habe ich keines gefunden), müsstest Du selber warten. Entweder die Methode blockiert (while-Chleife, mit Thread.Sleep(10))
Oder Du machst die Methode asynchron und startest dann einen neuen Task, der dann Wartet. Dann aber nicht mehr mit Thread.Sleep, sondern Task.Delay(10).

So erzeugst Du nicht ständig neue Threads, was sehr teuer ist und Du sparst dir die Kontrolle, dass immer nur ein Thread läuft.

Das müsste auch mit Tasks funktionieren und ist auch besser und einfacher.
Bei Tasks hast Du auch den Vorteil, dass es die ContinueWith-Methode gibt.

Eine spontane Idee: Währe nicht möglich, mit ContinueWith auf die Beendigung zu warten, dann warten, bis die 10 Sekunden abgelaufen sind und dann sich selber neu starten?
Nur eine Idee, getestet habe ich nichts



PS:
Ich hab mal ein Beispiel gebastelt. Hat sicherlich Verbesserungsbedarf von Jemandem, der mehr Erfahrung damit hat.
Es ist außerdem nur ein Beispiel, in einem Produktiv-System würde ich das nicht einbauen - schon allein wegen den Konsolenausgaben. :D

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:
50:
51:
52:
53:
54:
55:
56:
public class LoopTaskRunner
{
    private readonly Func<CancellationToken, Task> _asyncAction;
    private readonly object _lock = new object();
    private DateTime _lastStart;

    public DateTime LastStart
    {
        get
        {
            lock (_lock)
                return _lastStart;
        }
        private set
        {
            lock (_lock)
                _lastStart = value;
        }
    }

    public TimeSpan DelayForNextStart { get; }

    public LoopTaskRunner(Func<CancellationToken, Task> asyncAction, TimeSpan delayForNextStart)
    {
        _asyncAction = asyncAction;
        DelayForNextStart = delayForNextStart;
    }
        
    public async Task RunAsync(CancellationToken cancellationToken)
    {
        var counter = 0;

        while (!cancellationToken.IsCancellationRequested)
        {
            counter++;

            await Task.Factory.StartNew(async () =>
            {
                LastStart = DateTime.Now;
                Console.WriteLine($"{counter}: {DateTime.Now} | Starting");

                await _asyncAction(cancellationToken);

                Console.WriteLine();
                Console.WriteLine($"{counter}: {DateTime.Now} | {(cancellationToken.IsCancellationRequested ? "Cancled" : "Finished")}");
                Console.WriteLine(new string('='20));

                if (!cancellationToken.IsCancellationRequested)
                {
                    while (DateTime.Now - LastStart < DelayForNextStart)
                        await Task.Delay(50);
                }
            }).Unwrap();
        }
    }
}


Die Nutzung:

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:
static void Main(string[] args)
{
    DoSomethingAsync();

    Console.ReadKey();
}

static async Task DoSomethingAsync()
{
    var runner = new LoopTaskRunner(async token =>
    {
        for (int i = 0; i < 10; i++)
        {
            if (token.IsCancellationRequested)
                break;

            Console.Write(i);
            await Task.Delay(200);
        }
    }, TimeSpan.FromSeconds(5));

    var cancellationTokenSource = new CancellationTokenSource();

    var _ = runner.RunAsync(cancellationTokenSource.Token);

    await Task.Delay(TimeSpan.FromSeconds(11));
    cancellationTokenSource.Cancel();
}


Die Ausgabe sieht bei mir so aus:

Zitat:
1: 08.02.2016 13:10:02 | Starting
0123456789
1: 08.02.2016 13:10:04 | Finished
====================
2: 08.02.2016 13:10:07 | Starting
0123456789
2: 08.02.2016 13:10:09 | Finished
====================
3: 08.02.2016 13:10:12 | Starting
01234
3: 08.02.2016 13:10:13 | Cancled
====================
m.keller Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 133

Win xp, Win 7
C# (VS 2008)
BeitragVerfasst: Di 09.02.16 16:04 
Vielen dank für das Beispiel.

Ich habe ein Task erstellt.
Leider entpuppt sich dies nun als Problem.

Ich erstelle ein neuen Task der sich durch Ereignisse beendet.
Nun bekomme ich den ein System.OutOfMemoryException und das nach der 9 Messung.

Gibt der GC den Task nach beenden nicht frei?

in dem Task wird eine 32 bit .dll Unmanaged verwendet.
Kann es sein das dieses nicht sauber Freigegeben wird?
Zu mindestens wird dieser Funktion ein int[] bereitgestellt womit die Funktion arbeiten soll, bis der Task beendet wird.
Aber selbst .Dispose() von Task hilft nicht.

mit Thread ging es deutlich besser :-(
Oder ich stehe gerade am Schlauch.

_________________
Der gesunde Menschenverstand ist nur eine Anhäufung von Vorurteilen, die man bis zum 18. Lebensjahr erworben hat. (Albert Einstein)
m.keller Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 133

Win xp, Win 7
C# (VS 2008)
BeitragVerfasst: Mi 10.02.16 16:11 
okay, habe den fehler gefunden.

Es liegt scheinbar an der API vom Zulieferer.(Paralleler Zugriff auf zwei Funktionen mag die API nicht)

Somit erst ein mal erledigt, danke

_________________
Der gesunde Menschenverstand ist nur eine Anhäufung von Vorurteilen, die man bis zum 18. Lebensjahr erworben hat. (Albert Einstein)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 10.02.16 19:01 
Zitat:
Es liegt scheinbar an der API vom Zulieferer.(Paralleler Zugriff auf zwei Funktionen mag die API nicht)


Hattest du nicht geschrieben das es denn Thread immer nur einmal gibt? Woher sollte dann ein paralleler Zugriff kommen?

Hört sich eher für mich an das die API intern state hat der es nicht verträgt aus verschiedenen Threads (wel du z.B. immer wieder einen neuen Thread startest auch wenn es eine serielle Aufgabe ist) angesprochen zu werden, auch nicht nacheinander. Heißt für mich du solltest den Thread eben nicht immer wieder neustarten (egal ob Thread, Backgroundworker, Task, async/await, PLINQ oder welche Threading Feature du auch immer verwendest) sondern den wiederverwenden der das Laden der Dll ausgelöst hat.

Ist das ein c-style Dll (die du per DLLImport Atrribute nutzt) oder ein COM Object?
m.keller Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 133

Win xp, Win 7
C# (VS 2008)
BeitragVerfasst: Do 11.02.16 09:05 
Ja der Thread der der die Serielle Abarbeitung zuständig ist ist nur ein mal verfügbar.

Es gibt aber ein Befehl der API um die Verarbeitung ab zu brechen die dann den Thread durch eine Rückgabe der API beendet.
Dieser Befehl wird aus einer WCF Kommunikation ausgelöst.

Laut Hersteller der API ist diese nicht Threadsafe :-((weis ich seit gestern Abend)
Dort werden durch die aufrufe Events ausgelöst und diese werden verarbeitet. Sobald ein weiteres Event ausgelöst wird, knallt es.

Es ist ein c-style dll mit DLLImport, richtig.

Ich versuche die Funktionsaufrufe mit lock{} mal zu synchronisieren.
Vielleicht hilft das schon mal.

_________________
Der gesunde Menschenverstand ist nur eine Anhäufung von Vorurteilen, die man bis zum 18. Lebensjahr erworben hat. (Albert Einstein)