tutorials.de Buch-Aktion 05/2012
Like Tree1Danke
  • 1 Beitrag von MCoder
ERLEDIGT
NEIN
ANTWORTEN
11
ZUGRIFFE
594
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    Hallo zusammen,

    leider habe ich nichts passendes zu meinem Problem gefunden. Mein Problem ist, das mein Kopiervorgang sehr sehr langsam ist und ich mich jetzt frage, ob sich das den nicht beschleunigen lässt.

    In meinem Test habe ich 12 GB von einer externen HDD (USB 2.0) auf ein lokales Verzeichnis kopiert. Das ganze hat ca. 12 h gedauert. Wenn ich die selben Daten über Windows kopiert habe, hat das ganze nur ca. 3 h gedauert. Mir ist durch aus bewusst, dass Windows mit C geschrieben ist (glaube ich) und C# um einiges langsamer ist. Aber gibt es nicht irgend einen Trick um das Kopieren in C# zu beschleuigen. Ich denke so in die Richtung, dass man mehrere Kopiervorgänge startet (dürfen sich natürlich nicht beeinträchtigen) oder ist das absoluter Blödsinn.

    Hier mal der Weg wie ich das mache. Zuerst ermittel ich den Ziel- & Quellpfad. Anschließend rufe ich die Funktion CopyImages (selbst geschrieben) auf. Ich kopiere die Daten rekursiv. Verzeichnis für Verzeichnis, Datei für Datei. Dabei darf es nicht passieren, dass vorhanden Daten überschrieben werden. Nätürlich Loge ich alles mit und zeige es dem Benutzer in einem Windows Fenster, wo sich auch eine Processbar befindet, die nach jeder Kopie um eins erhöht wird.

    Hier ist die Funktion:

    Code :
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    
            /// <summary>
            /// Kopiert ein Ordner.
            /// </summary>
            /// <param name="destinationPath">Zielverzeichnis</param>
            /// <param name="sourcePath">Quellverzeichnis</param>
            /// <param name="fRecursive"><c>true:</c>Wenn auch Unterverzeichnisse
            /// mit kopiert werden soll</param>
            private void CopyImages(string destinationPath, string sourcePath,
                bool fRecursive)
            {
                //lokale Variablen
                int i = 0;
                int posSep = 0;
                string sDir = null;
                string[] aDirs = null;
                string sFile = null;
                string[] aFiles = null;
     
                Application.DoEvents();
     
                //Zeichen "\" hinzufügen, wenn nicht vorhanden ist
                if (!sourcePath.EndsWith(System.IO.Path.DirectorySeparatorChar.
                    ToString()))
                {
                    sourcePath += System.IO.Path.DirectorySeparatorChar;
                }
     
                if (!destinationPath.EndsWith(System.IO.Path.DirectorySeparatorChar.
                    ToString()))
                {
                    destinationPath += System.IO.Path.DirectorySeparatorChar;
                }
     
                if (fRecursive)
                {
                    //Alle Unterverzeichnis ermitteln
                    aDirs = System.IO.Directory.GetDirectories(sourcePath);
                    //Jeden Unterordner durchlaufen
                    for (i = 0; i <= aDirs.GetUpperBound(0); i++)
                    {
                        //ermittelt den letzen '\' im aktuellen Quellverzeichnis
                        posSep = aDirs[i].LastIndexOf(@"\");
                        //Name des Unterordners ermitteln
                        sDir = aDirs[i].Substring((posSep + 1), aDirs[i].Length - 
                            (posSep + 1));
                        //Verzeichnis neu erstellen
                        System.IO.Directory.CreateDirectory(destinationPath + sDir);
     
                        //Methode erneut aufrufen, um weitere Unterverzeichnis des 
                        //Unterverzeichnis zu kopieren
                        CopyImages((destinationPath + sDir), aDirs[i], fRecursive);
                    }
                }
     
                // Alle Dateien des aktuellen Verzeichnisses
                aFiles = System.IO.Directory.GetFiles(sourcePath);
     
               
                //Kopiert alle Dateien
                for (i = 0; i <= aFiles.GetUpperBound(0); i++)
                {
                    //ermittelt den letzen '\' im aktuellen Quellverzeichnis
                    posSep = aFiles[i].LastIndexOf(@"\");
     
                    //Dateinamen ermitteln
                    sFile = aFiles[i].Substring((posSep + 1), aFiles[i].Length - 
                        (posSep + 1));
     
                    try
                    {
                        //Datei kopieren
                        System.IO.File.Copy(aFiles[i], destinationPath + sFile,
                            false);
                        this.txtLog.Text += Properties.Resources.dgCopyPic01 +
                            sFile + Environment.NewLine + Properties.Resources.
                            dgCopyPic02 + destinationPath + Environment.NewLine;
                        Logfile.WriteLogEntry(Properties.Resources.dgCopyPic01 +
                           sFile + Environment.NewLine + Properties.Resources.
                           dgCopyPic02 + destinationPath);
                       
                    }
                    catch (Exception ex)
                    {
                        //Fehler beim Kopieren
                        //Log - Eintrag
                        Logfile.WriteLogEntry("=> " + ex.Message);
                        this.txtLog.Text += "=> "+ ex.Message + Environment.NewLine;
                    }
                    finally
                    {
                        //Fortschrittsleiste erhöhen
                        prbImprovment.PerformStep();
                        this.Refresh();
                    }
                }
            }

    Die Nachrichten für den Benutzer werden einer Resource Datei entnommen, weil mein Tool später in mehrere Sprachen übersetzt werden soll. Kommt bitte nicht auf die Idee zu sagen ich soll doch einfach, das manuell machen (also über Windows), dass geht nicht, weil der Kopiervorgang nur ein Teil eines großeren Firmenspezifischen Wiederherstellungstools ist und dies soll so weit wie möglich vollständig automatisiert werden.

    Kann mir ein erfahrender Programmiere vielleicht helfen? Ich habe nämlich noch keinerlei Erfahrung mit performance gerechter Programmierung.

    Vielen Dank

    Isabelle

    PS: Ich programmiere mit C# in .Net. Es handelt sich hier um eine Windowsanwendung
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

  2. #2
    MCoder MCoder ist offline Mitglied Diamant
    tutorials.de Premium-User
    Registriert seit
    Jul 2005
    Ort
    München
    Beiträge
    2.448
    Hallo Isabelle,

    zunächst solltest du die zugrundeliegende Struktur ändern und die Kopierroutine in einen separaten Thread auslagern. Das Ganze mit GUI-Aktionen zu vermischen und die Verwendung von Application.DoEvents() sind eine wirksame Bremse. Dazu bietet sich die BackgroundWorker-Klasse an. Fortschritts- bzw. Loginfos können per Event (ProgressChanged) an die GUI übermittelt werden. Schaue dir einfach die Klassenreferenz in der MSDN an, die Anendung ist ganz gut dokumentiert.

    Pfadkomponenten kannst du übrigends einfach mit System.IO.Path.Combine() miteinander verbinden. Dabei werden evt. fehlende Pfadtrenner (Backslash) automatisch ergänzt.

    Gruß
    MCoder
    Bella_Isy bedankt sich. 
    "The three chief virtues of a programmer are: Laziness, Impatience and Hubris."
    --- Larry Wall

  3. #3
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    Hallo MCoder,

    vielen Dank für deine Hilfe. Ich werde mal in der MSDN nachlesen. Application.DoEvents() habe ich nur eingefügt, damit der entsprechende Dialog meiner Anwendung sich nicht aufhängt. Ich muss nämlich den Benutzer im Dialog in einem Textfeld jeden Kopierschritt mitteilen. Möchte mein Chef (Welche Datei, wohin kopiert wurde, usw). Geht das alles mit dem Event oder mich ich mir da extra definieren?

    Vielen Dank

    Isabelle
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

  4. #4
    MCoder MCoder ist offline Mitglied Diamant
    tutorials.de Premium-User
    Registriert seit
    Jul 2005
    Ort
    München
    Beiträge
    2.448
    Zitat Zitat von Bella_Isy Beitrag anzeigen
    Application.DoEvents() habe ich nur eingefügt, damit der entsprechende Dialog meiner Anwendung sich nicht aufhängt. Ich muss nämlich den Benutzer im Dialog in einem Textfeld jeden Kopierschritt mitteilen.
    Wie schon gesagt, so sollte man keinesfalls programmieren. Alle Anzeigen, Ausgaben usw. kannst du an den ProgressChanged-Event des BackgroundWorkers hängen. Damit werden auch die Aufgaben (Kopieren und Dokumentieren) sauber voneinander getrennt. Wahrscheinlich musst du dir noch eine Klasse von "ProgressChangedEventArgs" ableiten, um deine eigenen Information dort unterzubringen.

    Gruß
    MCoder
     
    "The three chief virtues of a programmer are: Laziness, Impatience and Hubris."
    --- Larry Wall

  5. #5
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    ich danke dir für deine Antwort. Ich habe mir den entsprechende MSDN Teil schon durchgelesen. Das ist ja garnicht so schwierig wie ich gedacht habe. Mal sehn, ob es dann auch so einwandfrei funktioniert.

    Nochmals vielen Dank

    Isabelle
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

  6. #6
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    Hallo zusammen,

    ich habe jetzt meine Anwendung so umgeschrieben, dass das Kopieren im Hintergrund ausgeführt wird. Doch nun habe ich das Problem, das nun nach einer Zeit die Ausnahme StackOverflowException, bei schreiben der Log Einträge, ausgelösst wird und ich weiß nicht was ich noch machen soll. Ich muss die Dateien rekursiv kopieren, weil ich nicht weiß wieviele Unterverzeichnisse der jeweilige Ordner hat und ausserdem muss jede Kopie mit protokolliert werden. (Welche Datei wird kopiert? Wohin wird diese kopiert?)

    Was kann ich machen. Hier noch mein Code
    Code :
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    
            /// <summary>
            /// Kopiert ein Ordner.
            /// </summary>
            /// <param name="destinationPath">Zielverzeichnis</param>
            /// <param name="sourcePath">Quellverzeichnis</param>
            /// <param name="fRecursive"><c>true:</c>Wenn auch Unterverzeichnisse
            /// mit kopiert werden soll</param>
            private void CopyImages(string destinationPath, string sourcePath,
                bool fRecursive)
            {
                //lokale Variablen
                int i = 0;
                int posSep = 0;
                string sDir = null;
                string[] aDirs = null;
                string sFile = null;
                string[] aFiles = null;
                Properties.Settings s = new Properties.Settings();
                int percent = 0;
                string message = string.Empty;
               
     
                if (fRecursive)
                {
                    //Alle Unterverzeichnis ermitteln
                    aDirs = System.IO.Directory.GetDirectories(sourcePath);
                    //Jeden Unterordner durchlaufen
                    for (i = 0; i <= aDirs.GetUpperBound(0); i++)
                    {
                        //ermittelt den letzen '\' im aktuellen Quellverzeichnis
                        posSep = aDirs[i].LastIndexOf(@"\");
                        //Name des Unterordners ermitteln
                        sDir = aDirs[i].Substring((posSep + 1), aDirs[i].Length -
                            (posSep + 1));
                        //Verzeichnis neu erstellen
                        Directory.CreateDirectory(Path.Combine(destinationPath,sDir));
     
                        //Methode erneut aufrufen, um weitere Unterverzeichnis des 
                        //Unterverzeichnis zu kopieren
                        CopyImages(Path.Combine(destinationPath,sDir), aDirs[i], fRecursive);
                    }
                }
                // Alle Dateien des aktuellen Verzeichnisses
                aFiles = System.IO.Directory.GetFiles(sourcePath);
     
     
                /*Copy all files.*/
                for (i = 0; i <= aFiles.GetUpperBound(0); i++)
                {
                    //ermittelt den letzen '\' im aktuellen Quellverzeichnis
                    posSep = aFiles[i].LastIndexOf(@"\");
     
                    //Dateinamen ermitteln
                    sFile = aFiles[i].Substring((posSep + 1), aFiles[i].Length -
                        (posSep + 1));
     
                    try
                    {
                        //Datei kopieren
                        System.IO.File.Copy(aFiles[i], Path.Combine(destinationPath,sFile),
                            false);
                        s.counter+= 1;
                        //Log Eintrag
                        //Prozenzsatz ermitteln
                        percent = s.counter * 100;
                        percent = percent / s.MaxProgress;
                        //Nachricht 
                        message = Properties.Resources.dgCopyPic01 + sFile +
                            Environment.NewLine + Properties.Resources.dgCopyPic02 +
                            destinationPath;
                        backgroundWorker1.ReportProgress(percent, message);
                    }
                    catch (Exception ex)
                    {
                        //Fehler beim Kopieren
                        //Log - Eintrag
                        s.counter += 1;
                        //Prozenzsatz ermitteln
                        percent = s.counter * 100;
                        percent = percent / s.MaxProgress;
                        //Nachricht 
                        message = ex.Message;
                        backgroundWorker1.ReportProgress(percent, message);
                    }
                }
            }
     
            /// <summary>
            /// Tritt ein, wenn die Methode RunWorkerAsync() aufgerufen wird
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                //Dateien kopieren
                this.CopyImages(this.lblPicPath.Text,this.lblImageBackup.Text,true);
     
            }
     
             /// <summary>
            /// Tritt ein, wenn der Report geschrieben wird
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void backgroundWorker1_ProgressChanged(object sender, 
                ProgressChangedEventArgs e)
            {
                //Log Eintrag 
                this.txtLog.Text += (string)e.UserState + Environment.NewLine; //<- Hier tritt der Fehler nach einiger Zeit auf
                //Fortschrittsleiste erhöhen
                prbImprovment.PerformStep();
            }

    In der MSDN habe ich mir den Eintrag "Problembehandlung bei Ausnahmen: System.StackOverflowException" angeschaut, um das Problem zu lösen.
    Da wird geschrieben, das man nicht so viel rekursiv aufrufen soll oder wenn es den sein muss das man die Common Language Runtime entladen soll. Ich weiß nicht was ich machen soll.

    Wenn ich die Log nicht schreiben lassen, tritt kein Fehler auf. Die Geschwindingkeit passt auch.

    Hat jemand eine Idee, wie ich das ganze mit Logeinträge zum laufen bringe.

    Vielen Dank

    Isabelle
    Geändert von Bella_Isy (07.07.10 um 14:06 Uhr)
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

  7. #7
    MCoder MCoder ist offline Mitglied Diamant
    tutorials.de Premium-User
    Registriert seit
    Jul 2005
    Ort
    München
    Beiträge
    2.448
    Hallo Isabelle,

    der Fehler ist ein Indiz dafür, das die Protokollierung ab einer bestimmten Datenmenge länger dauert, als das Kopieren (womit wohl auch schon der Grund für den vorherigen Geschwindigkeitseinbruch gefunden wäre). Versuche mal, statt "+=" die Methode "AppendText" zu verwenden, da diese wesentlich performanter ist.
    Code csharp:
    1
    
    this.txtLog.AppendText((string)e.UserState + Environment.NewLine);
    Gruß
    MCoder
     
    "The three chief virtues of a programmer are: Laziness, Impatience and Hubris."
    --- Larry Wall

  8. #8
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    Hallo,

    ich danke dir. Es hat einwandfrei funktioniert.

    Kannst du mir auch den Unterschied zwischen den beiden Methoden erklären, mich interessiert es nämlich auch die Hindergründe zu verstehen von dem was ich mache.

    Vielen Dank

    Isabelle
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

  9. #9
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    Hallo,

    gibt es den auch eine alternative zu

    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    
    //Log-Eintrag anfügen
    using (StreamWriter sw = new StreamWriter(path, true))
    {
        sw.WriteLine(message);
        sw.WriteLine();
        sw.Close();
    }

    da habe ich nämlich genau das gleiche Problem

    Gruß Isabelle
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

  10. #10
    deepthroat deepthroat ist offline Mitglied Diamant
    tutorials.de Premium-User
    Registriert seit
    Jun 2005
    Beiträge
    8.168
    Hi.
    Zitat Zitat von Bella_Isy Beitrag anzeigen
    Hallo,

    gibt es den auch eine alternative zu

    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    
    //Log-Eintrag anfügen
    using (StreamWriter sw = new StreamWriter(path, true))
    {
        sw.WriteLine(message);
        sw.WriteLine();
        sw.Close();
    }

    da habe ich nämlich genau das gleiche Problem
    Heißt das du öffnest und schließt jedesmal wenn du eine Nachricht in die Logdatei schreiben willst die Datei?

    Öffne die Datei am Anfang vom Programm und schließe sie erst wenn du fertig bist.

    Gruß
     
    If at first you don't succeed, try again. Then quit. No use being a damn fool about it.

  11. #11
    MCoder MCoder ist offline Mitglied Diamant
    tutorials.de Premium-User
    Registriert seit
    Jul 2005
    Ort
    München
    Beiträge
    2.448
    Zitat Zitat von Bella_Isy Beitrag anzeigen
    Kannst du mir auch den Unterschied zwischen den beiden Methoden erklären, mich interessiert es nämlich auch die Hindergründe zu verstehen von dem was ich mache.
    Offensichtlich ist die Speicher-Realloziierung bei der Stringverknüpfung mit "AppendText()" besser gelöst als mit "+=".

    Gruß
    MCoder
     
    "The three chief virtues of a programmer are: Laziness, Impatience and Hubris."
    --- Larry Wall

  12. #12
    Bella_Isy Bella_Isy ist offline Mitglied Gold
    Registriert seit
    Mar 2005
    Beiträge
    114
    Hallo,

    ich habe es jetzt umgestellt, dass ich die Datei nur einmal öffne und am Ende wieder schließe.

    Ich Danke euch für eure Hilfe

    Vielen Dank, ihr habt mir sehr geholfen
     
    Achtung******!! Diese Nachricht kommt von einer Frau.

Ähnliche Themen

  1. Exception bei File.Delete nach File.Copy - Wie umgehen?
    Von Asterix-Ac im Forum .NET Datenverwaltung
    Antworten: 5
    Letzter Beitrag: 06.12.07, 14:23
  2. copy-Funktion sehr langsam?
    Von soyo im Forum PHP
    Antworten: 6
    Letzter Beitrag: 16.07.06, 22:36
  3. PC sehr langsam
    Von joners im Forum Hardware
    Antworten: 4
    Letzter Beitrag: 17.09.04, 17:37
  4. redhat sehr langsam
    Von Draxx im Forum Linux & Unix
    Antworten: 3
    Letzter Beitrag: 21.06.04, 08:51
  5. wmi sehr langsam
    Von stormystormy im Forum Sonstige Sprachen
    Antworten: 0
    Letzter Beitrag: 02.01.04, 15:37