Fragensteller
C#: Mutli-Threading Dateiserialisierung (await/async)

Allgemeine Diskussion
-
Nabend beisammen,
ich wage mich gerade für ein kleines Programm an die async/await-Methodik ran. Allerdings scheitere ich an der Umsetzung. Beziehungs gelingt die Umsetzung, aber nicht so, wie ich es mir vorstelle.
Ich möchte, dass die Bilder, die ich in einem OpenFileDialog auswähle, im Hintergrund asynchron gleichzeitig eingelesen und kurz bearbeitet werden (Methode SourceImage.Load). Sobald ein Bild fertig serialisiert ist, soll es zu einer Liste hinzugefügt werden, die als ItemSource für eine ListView dient. Der UI Thread soll natürlich responsible bleiben. Allerdings erhalte ich den Fehler, dass die DependencySource nicht auf einem anderen Thread liegen darf. Wie muss ich meinen Code ändern, um o.g. Ziel zu erreichen?
Das ist im OpenFileDialog-Result Teil:
foreach (SourceImage img in Task.WhenAll(LoadImages(fileDialog.FileNames)).Result.First()) { SourceImages.Add(img); }
So sieht die Methode LoadImages aus:
private async Task<List<SourceImage>> LoadImages(string[] images) { List<Task<SourceImage>> taskList = new List<Task<SourceImage>>(); taskList = (from file in images select SourceImage.Load(file, StartNumber)).ToList(); List<SourceImage> imgList = new List<SourceImage>(); while (taskList.Count > 0) { Task<SourceImage> imageTask = await Task.WhenAny(taskList); taskList.Remove(imageTask); SourceImage image = await imageTask; imgList.Add(image); this.Dispatcher.Invoke((Action)(() => { LoadingImageStatus.Value++; })); } return imgList; }
Und die SourceImage.Load Methode (die sehr ressourcenfressend und zeitaufwendig ist):
public static async Task<SourceImage> Load(string path, int StartNumber = 1) { SourceImage img = new SourceImage(); await Task.Run(() => { using (var image = Image.FromFile(path)) { //img.Image = new Bitmap(bmpTemp); System.Drawing.Size thumbSize = SourceImage.GetThumbnailSize(image); Image thumbnail = image.GetThumbnailImage(thumbSize.Width, thumbSize.Height, null, IntPtr.Zero); Bitmap bmp = new Bitmap(thumbnail); MemoryStream ms = new MemoryStream(); bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); ms.Position = 0; BitmapImage bi = new BitmapImage(); bi.BeginInit(); bi.StreamSource = ms; bi.EndInit(); img.ThumbnailImage = bi; } //img.ThumbnailImage = new BitmapImage(new Uri(path)); img.Path = path; img.Title = System.IO.Path.GetFileName(path); img.Number = CurrentIndex + StartNumber; CurrentIndex++; }); return img; }
Was mache ich falsch? Ich würde mich äußerst über konstruktive Hilfe freuen.
Grüße
- Typ geändert Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 7. Juni 2016 08:40
Alle Antworten
-
Hallo,
alles was aus dem UI Thread aufgerufen wird (also auch die gebundene Liste) muss im UI Thread erzeugt worden sein. Das ist bei dir vermutlich der Fall.
Problematisch wird es nun wenn du aus anderen Threads auf den UI-Thread zugreifen willst. Hierfür musst du nun über den Dispatcher gehen:
foreach (SourceImage img in Task.WhenAll(LoadImages(fileDialog.FileNames)).Result.First()) { Dispatcher.Invoke(()=>{ SourceImages.Add(img); }); }
Dadurch wird der Add-Aufruf im UI-Thread durchgeführt. Die foreach-Schleife jedoch weiterhin in den einzelnen Threads.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
Vielen Dank schonmal.
Das behebt den Fehler, aber den gewünschten Effekt, den ich erzielen wollte, erreicht es nicht: Das UI friert nach wie vor ein und es braucht eine Ewigkeit, bis auf einmal alle Bilder in der ListView auftauchen (die ListView, deren ItemSource SourceImages ist).
Aber ich hatte es eher so gedacht, dass das UI responsive bleibt und die Bilder immer aufploppen, wenn sie fertig sind.