Ich arbeite an einer Anwendung, die große Mengen von Textdaten verarbeitet und Statistiken über das Vorkommen von Wörtern erstellt (siehe: Quellcode-Wortwolke ).
Der vereinfachte Kern meines Codes sieht folgendermaßen aus.
- Aufzählung aller Dateien mit der Erweiterung *.txt.
- Aufzählung der Wörter in den einzelnen Textdateien.
- Nach Wort gruppieren und Vorkommen zählen.
- Nach Vorkommen sortieren.
- Ausgabe Top 20.
Mit LINQ hat alles gut funktioniert. Der Wechsel zu PLINQ brachte mir einen deutlichen Leistungsschub. Aber ... die Abbrechbarkeit bei lang laufenden Abfragen ist verloren.
Es scheint, dass die OrderBy-Abfrage Daten zurück in den Hauptthread synchronisiert und Windows-Nachrichten nicht verarbeitet werden.
Im folgenden Beispiel zeige ich meine Umsetzung der Kündigung nach MSDN How to: Abbrechen einer PLINQ-Abfrage was nicht funktioniert :(
Haben Sie eine andere Idee?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace PlinqCancelability
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
m_CancellationTokenSource = new CancellationTokenSource();
}
private readonly CancellationTokenSource m_CancellationTokenSource;
private void buttonStart_Click(object sender, EventArgs e)
{
var result = Directory
.EnumerateFiles(@"c:\temp", "*.txt", SearchOption.AllDirectories)
.AsParallel()
.WithCancellation(m_CancellationTokenSource.Token)
.SelectMany(File.ReadLines)
.SelectMany(ReadWords)
.GroupBy(word => word, (word, words) => new Tuple<int, string>(words.Count(), word))
.OrderByDescending(occurrencesWordPair => occurrencesWordPair.Item1)
.Take(20);
try
{
foreach (Tuple<int, string> tuple in result)
{
Console.WriteLine(tuple);
}
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
}
private void buttonCancel_Click(object sender, EventArgs e)
{
m_CancellationTokenSource.Cancel();
}
private static IEnumerable<string> ReadWords(string line)
{
StringBuilder word = new StringBuilder();
foreach (char ch in line)
{
if (char.IsLetter(ch))
{
word.Append(ch);
}
else
{
if (word.Length != 0) continue;
yield return word.ToString();
word.Clear();
}
}
}
}
}