В моем коде есть асинхронный класс, который получает данные из электронной таблицы (например, номера социального страхования и номера контрактов), удаляет данные с веб-сайта, возвращает эти данные как объект, а затем записывает этот объект в Excel. электронная таблица.
Однако я столкнулся со следующей проблемой: я использую семафор, чтобы определить, сколько экземпляров будет работать одновременно. Пока все хорошо, окей. Но когда я запускаю два экземпляра одновременно, информация, записанная в таблице Excel, повторяется. Я заметил, что каждая запись повторяется случайным образом (но никогда не превышает предел семафора (это любопытно).
Я уже пытался определить ConcurrentQueue как потокобезопасный, но это не очень помогло. Моя цель - удалить данные и записать все в таблицу Excel, но удалить несколько записей одновременно (а не ждать, пока одна запись будет завершена, чтобы начать другую).
Я также пытался писать пакеты (каждые 10 обработанных записей записываются в файл, но это не помогло).
Это мой код для ProcessFileService:
public class ProcessFileService : IProcessFileService
{
private readonly IFileService _fileService;
private readonly IValidationService _validationService;
private readonly IWebScrapingService _webScrapingService;
private readonly IExcelWriterService _excelWriterService;
private static SemaphoreSlim _semaphore = new SemaphoreSlim(5, 5); // 5 initial, 5 max
private static ConcurrentDictionary _processedEntries = new ConcurrentDictionary();
public ProcessFileService(IFileService fileService, IValidationService validationService, IWebScrapingService webScrapingService, IExcelWriterService excelWriterService)
{
_fileService = fileService;
_validationService = validationService;
_webScrapingService = webScrapingService;
_excelWriterService = excelWriterService;
}
public async Task ProcessFile(string filePath, Action updateStatus)
{
var lines = _fileService.ReadFile(filePath);
updateStatus("Leitura do arquivo concluída. Processando...");
var validationResults = _validationService.ValidateFile(lines);
// Use a ConcurrentQueue to safely handle concurrent access
var queue = new ConcurrentQueue(validationResults.Take(20)); // process only 20 entries for example
updateStatus($"Processando {queue.Count} entradas");
List tasks = new List();
Random rnd = new Random();
while (queue.Count > 0)
{
if (queue.TryDequeue(out var line))
{
var cpfCnpj = line[1];
var numAcordo = line[0];
// Check if the entry has already been processed
if (_processedEntries.TryAdd(cpfCnpj, numAcordo))
{
tasks.Add(Task.Run(async () =>
{
await _semaphore.WaitAsync();
try
{
updateStatus($"Processando {numAcordo} \\" + $" {cpfCnpj}");
await Task.Delay(rnd.Next(1398, 2646));
// Process the entry with IWebScrapingService
AcordoData acordoData;
try
{
acordoData = await _webScrapingService.ProcessValidEntries(numAcordo, cpfCnpj);
}
catch (Exception ex)
{
updateStatus($"Error in IWebScrapingService for {numAcordo}: {ex.Message}");
return; // Skip to the next item in the queue
}
// Write to Excel
try
{
string path = @"C:\SAIDA_PROGRAMA\";
string fileName = "SAIDA_BUSCADADOS.xlsx";
string finalPath = System.IO.Path.GetFullPath(path + "\\" + fileName);
await _excelWriterService.WriteToExcel(acordoData, finalPath);
}
catch (Exception ex)
{
updateStatus($"IExcelWriterService error {numAcordo}: {ex.Message}");
}
// Optionally log or update status
updateStatus($"Processado {numAcordo} com sucesso.");
}
catch (Exception ex)
{
updateStatus($"Erro processando {numAcordo}: {ex.Message}");
}
finally
{
_semaphore.Release();
}
}));
}
}
}
await Task.WhenAll(tasks);
}
}
И это класс, который пишет все:
public class ExcelWriterService : IExcelWriterService
{
private static readonly object _fileLock = new object();
public async Task WriteToExcel(AcordoData acordoData, string filePath)
{
await Task.Run(() =>
{
lock (_fileLock)
{
IWorkbook workbook;
ISheet sheet;
bool fileExists = File.Exists(filePath);
if (fileExists)
{
// Open the existing file
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
workbook = new XSSFWorkbook(fs);
}
sheet = workbook.GetSheetAt(0);
}
else
{
// Create a new file
workbook = new XSSFWorkbook();
sheet = workbook.CreateSheet("AcordoData");
// Add headers
IRow headerRow = sheet.CreateRow(0);
headerRow.CreateCell(0).SetCellValue("Número Acordo");
headerRow.CreateCell(1).SetCellValue("Número CPF ou CNPJ");
headerRow.CreateCell(2).SetCellValue("Dias de atraso");
headerRow.CreateCell(3).SetCellValue("Situação");
headerRow.CreateCell(4).SetCellValue("Data Interrupção / Primeira parcela em aberto");
headerRow.CreateCell(5).SetCellValue("Saldo Acordo");
}
// Find the next available row
int rowNum = sheet.LastRowNum + 1;
IRow row = sheet.CreateRow(rowNum);
// Add data
row.CreateCell(0).SetCellValue(acordoData.NumAcordo);
row.CreateCell(1).SetCellValue(acordoData.CpfCnpj);
row.CreateCell(2).SetCellValue(acordoData.DiasEmAtraso);
row.CreateCell(3).SetCellValue(acordoData.Situacao);
if (acordoData.Situacao == "Interrompido")
{
row.CreateCell(4).SetCellValue(acordoData.DataInterrupcao);
}
else if (acordoData.Situacao == "Em Atraso" || acordoData.Situacao == "Em Dia")
{
row.CreateCell(4).SetCellValue(acordoData.ParcelData.Vencimento);
}
row.CreateCell(5).SetCellValue(acordoData.SaldoAcordo);
// Write the file
if (!Directory.Exists(Path.GetDirectoryName(filePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
}
using (var fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
workbook.Write(fs);
}
}
});
}
}
Подробнее здесь: https://stackoverflow.com/questions/788 ... ng-entries