Я создаю консольное приложение, которое должно обрабатывать пачку документов.
Для простоты, процесс выглядит так:
- для каждого года между X и Y,запросить БД, чтобы получить список ссылок на документы для процесса
- для каждой из этих ссылок, обработать локальный файл
Метод процесса, я думаю, независим и должен быть распараллеленкак только входные аргументы различаются:
private static bool ProcessDocument(
DocumentsDataset.DocumentsRow d,
string langCode
)
{
try
{
var htmFileName = d.UniqueDocRef.Trim() + langCode + ".htm";
var htmFullPath = Path.Combine("x:\path", htmFileName;
missingHtmlFile = !File.Exists(htmFullPath);
if (!missingHtmlFile)
{
var html = File.ReadAllText(htmFullPath);
// ProcessHtml is quite long : it use a regex search for a list of reference
// which are other documents, then sends the result to a custom WS
ProcessHtml(ref html);
File.WriteAllText(htmFullPath, html);
}
return true;
}
catch (Exception exc)
{
Trace.TraceError("{0,8}Fail processing {1} : {2}","[FATAL]", d.UniqueDocRef, exc.ToString());
return false;
}
}
Для перечисления моего документа у меня есть следующий метод:
private static IEnumerable<DocumentsDataset.DocumentsRow> EnumerateDocuments()
{
return Enumerable.Range(1990, 2020 - 1990).AsParallel().SelectMany(year => {
return Document.FindAll((short)year).Documents;
});
}
Document
- это бизнес-класс, который переносит поискдокументы.Результатом этого метода является типизированный набор данных (я возвращаю таблицу документов).Метод ожидает год, и я уверен, что документ не может быть возвращен более чем на один год (на самом деле год является частью ключа).
Обратите внимание на использование AsParallel()
здесь, ноУ меня никогда не было проблем с этим.
Теперь мой основной метод:
var documents = EnumerateDocuments();
var result = documents.Select(d => {
bool success = true;
foreach (var langCode in new string[] { "-e","-f" })
{
success &= ProcessDocument(d, langCode);
}
return new {
d.UniqueDocRef,
success
};
});
using (var sw = File.CreateText("summary.csv"))
{
sw.WriteLine("Level;UniqueDocRef");
foreach (var item in result)
{
string level;
if (!item.success) level = "[ERROR]";
else level = "[OK]";
sw.WriteLine(
"{0};{1}",
level,
item.UniqueDocRef
);
//sw.WriteLine(item);
}
}
Этот метод работает, как и ожидалось в этой форме.Однако, если я заменю
var documents = EnumerateDocuments();
на
var documents = EnumerateDocuments().AsParrallel();
Он перестает работать, и я не понимаю, почему.
Ошибка появляется именно здесь (вмой метод обработки):
File.WriteAllText(htmFullPath, html);
Он говорит мне, что файл уже открыт другой программой.
Я не понимаю, что может привести к тому, что моя программа не будет работать должным образом.Поскольку моя documents
переменная IEnumerable
возвращает уникальные значения, почему мой метод процесса не работает?
thx для рекомендаций
[Редактировать] Код для получения документа:
/// <summary>
/// Get all documents in data store
/// </summary>
public static DocumentsDS FindAll(short? year)
{
Database db = DatabaseFactory.CreateDatabase(connStringName); // MS Entlib
DbCommand cm = db.GetStoredProcCommand("Document_Select");
if (year.HasValue) db.AddInParameter(cm, "Year", DbType.Int16, year.Value);
string[] tableNames = { "Documents", "Years" };
DocumentsDS ds = new DocumentsDS();
db.LoadDataSet(cm, ds, tableNames);
return ds;
}
[Edit2] Возможный источник моей проблемы, благодаря mquander.Если я написал:
var test = EnumerateDocuments().AsParallel().Select(d => d.UniqueDocRef);
var testGr = test.GroupBy(d => d).Select(d => new { d.Key, Count = d.Count() }).Where(c=>c.Count>1);
var testLst = testGr.ToList();
Console.WriteLine(testLst.Where(x => x.Count == 1).Count());
Console.WriteLine(testLst.Where(x => x.Count > 1).Count());
Я получу такой результат:
0
1758
Удаление AsParallel возвращает тот же вывод.
Вывод: мои EnumerateDocuments что-то неправильно и возвращаетпо два раза в каждом документе.
Мне нужно погрузиться здесь, я думаю
Это, вероятно, мой источник перечисления в деле