Ошибки при многопоточной записи строк в фаил/ы c#

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Всем привет, третий день бьюсь с проблемой. Я никогда не работал с многопотоком тем более на C#, решил начать с простенького проекта для которого я попросил chatgpt написать код, и вроде бы он даже работает во многопотоке но меня беспокоит что иногда вижу ошибки записи в фаил.


Сам проект сначала берёт рандомный фаил из папки(в папке ~100 фаилов по 1mb) и берёт из этого фаила строку с удалением. Тут вроде бы всё нормально и срабатывает механизм lock если я правильно понимаю суть его работы.
C#:
string inputDir = project.Directory + @"\files\input\";
project.Variables["inputDir"].Value = inputDir;

string outputDir = project.Directory + @"\files\output\";
project.Variables["outputDir"].Value = outputDir;

// Берём рандомный фаил из директории
//var inputDir = $"{project.Directory}{project.Variables["inputDir"].Value}";
var inputFile = ZennoLab.Macros.FileSystem.DirectoryRandomFile(inputDir);

// Проверяем количество файлов
if (inputFile.Length == 0)
{
    throw new Exception($"Не найдено ни одного файла в папке: {inputDir}");
}
project.Variables["inputFile"].Value = inputFile;

// Получаем имя файла
string fileName = Path.GetFileName(inputFile);
project.Variables["fileName"].Value = fileName;

Следующий экшен тоже в C# кубике делает Get запрос на полученную строку из фаила, разбивает ответ на заголовки и содержимое
C#:
var proxy = project.GetProxy(); // Получаем прокси проекта
var timeout = 7000;


//Сам запрос
    var response = ZennoPoster.HTTP.Request(
        ZennoLab.InterfacesLibrary.Enums.Http.HttpMethod.GET, project.Variables["inputLink"].Value, "", "", proxy, "UTF-8",
        ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.HeaderAndBody, timeout, "", project.Profile.UserAgent, true, 5,
        new String[]{}, "", false, false, project.Profile.CookieContainer);

// Обрабатываем полученный ответ сервера
response = response.Trim();

// Разделитель заголовков и содержимого
string[] separator = new string[] { "\r\n\r\n" };
// Массив заголовков и содержимого (содержимое тоже может быть разбито на части)
string[] headersAndBody = response.Split(separator, StringSplitOptions.RemoveEmptyEntries);

// Переменные для заголовков и содержимого
string headers = string.Empty;
string body = string.Empty;
bool headersEnd = false;


foreach (string piece in headersAndBody)
{
    // Если заголовки ещё не закончились и текущая часть начинается с 'HTTP/' (независимо от регистра)
    if (!headersEnd && piece.StartsWith("HTTP/", StringComparison.OrdinalIgnoreCase))
    {
        // Это ещё заголовки
        headers += separator[0] + piece;
    }
    else
    {
        // Это уже содержимое
        body += separator[0] + piece;
        headersEnd = true;
    }
}

// Избавляемся от пробелов и переводов строк в начале и конце заголовков и содержимого
headers = headers.Trim();
body = body.Trim();

// Записываем результаты (заголовки и содержимое по отдельности) в переменные проекта
project.Variables["splitted_headers"].Value = headers;
project.Variables["splitted_body"].Value = body;
Следующий экшен берёт последний статус код из заголовка


Потом идёт кубик C# который парсит тело ответа на разные условия и в зависимости от найденного условия делает запись в нужный фаил

пример условий:

{ "<title>Access denied</title>", "Access-denied.txt" },
{ "<title>Request Rejected</title>", "Access-denied.txt" },
{ "<h1>Not Found</h1>", "Not-Found.txt" },
{ "<title>404 Not Found</title>", "Not-Found.txt" },
{ "<title>Domain Suspension</title>", "Domain-Expired.txt" },
{ "<title>Account Suspended</title>", "Domain-Expired.txt" },

если ничего не спарсилось то тогда происходит запись в фаил Unknown.txt

всё бы ничего но тут какая то ошибка, потому что зенка пишет:
Выполнение действия CSharp OwnCode: Parsing All stuff. Процесс не может получить доступ к файлу "E:\BASE\(другой софт)\BadUrlsSorter\files\output\Unknown.txt", так как этот файл используется другим процессом.
www.png


Из чего я делаю вывод что некоторые строки не записываются, хотя сам процесс идёт довольно бодро и все фаилы наполняються визуально достаточно быстро. Я запускаю этот проект в 200 потоков и вижу как иногда лезут ошибки.

C#:
string outputDirectory = Path.Combine(project.Directory, "files", "output");

Dictionary<string, string> searchStringToFileMap = new Dictionary<string, string>()
{
    { "<title>Access denied</title>", "Access-denied.txt" },
    { "<title>Request Rejected</title>", "Access-denied.txt" },
    { "<title>Connection denied by Geolocation</title>", "Access-denied.txt" },
    // Добавьте другие строки поиска и файлы для записи результатов здесь...
};

bool matchFound = false;

string splittedBody = project.Variables["splitted_body"].Value;

if (!string.IsNullOrEmpty(splittedBody))
{
    string[] lines = splittedBody.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

    HashSet<string> searchStrings = new HashSet<string>(searchStringToFileMap.Keys);

    foreach (string line in lines)
    {
        foreach (string searchString in searchStrings)
        {
            if (line.Contains(searchString))
            {
                string fileName = searchStringToFileMap[searchString];
                string filePath = Path.Combine(outputDirectory, fileName);

                // Блокируем доступ к файлу с использованием lock для безопасной записи
                lock (project) // Можно использовать любой объект для блокировки, например, project
                {
                    File.AppendAllText(filePath, project.Variables["inputLink"].Value + Environment.NewLine);
                }

                matchFound = true;
                break;
            }
        }

        if (matchFound)
        {
            break;
        }
    }
}

if (!matchFound)
{
    string defaultFilePath = Path.Combine(outputDirectory, "Unknown.txt");
    File.AppendAllText(defaultFilePath, project.Variables["inputLink"].Value + Environment.NewLine);
}
Как мне исправить код, чтобы он не терял строки и записывал их в указанные фаилы?

Я пытался всё это время исправить этот код в chatgpt но он упорно подсовывает мне один и тот же код либо иногда пишет код который я так понимаю надо записывать там где записываются директивы using, я с этим ещё не работал и не разбирался, для меня это очень сложно.

Из сложившейся ситуации я сделал вывод что строки теряются потому что на запись тоже нужно делать lock. Я никогда не работал с этим, могу что то не так интерпритировать, но с chatgpt это не вывозит, помогите плиз кто чем может.
 

Midnight

Client
Регистрация
28.02.2016
Сообщения
320
Благодарностей
183
Баллы
43
Создать списки в ЗП, привязать их к нужным файлам и заменить это

C#:
                // Блокируем доступ к файлу с использованием lock для безопасной записи
                lock (project) // Можно использовать любой объект для блокировки, например, project
                {
                    File.AppendAllText(filePath, project.Variables["inputLink"].Value + Environment.NewLine);
                }
на это
C#:
                project.Lists[fileName].Add(project.Variables["inputLink"].Value);
 
  • Спасибо
Реакции: zerosearcher

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Демонстрация проблемы:
 

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Создать списки в ЗП, привязать их к нужным файлам и заменить это
в project.Lists[fileName] хранятся имена фаилов с расширением .txt
C#:
Domain-Expired.txt
Access-denied.txt
Not-Found.txt
и т.д.
я попробовал создать такие списки, но зенка убирает точку

в последнем экшене, который парсит боди из ответа на условия, заменил

C#:
File.AppendAllText(filePath, project.Variables["inputLink"].Value + Environment.NewLine);
на

C#:
project.Lists[fileName].Add(project.Variables["inputLink"].Value);
120904
 

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Я хочу проверить большое количество ссылок на разные условия.
Для каждого условия свой отдельный фаил для записи.
Если хотя бы одно условие выполнилось, то есть спарсился текст из тела ответа, например

C#:
<title>Access denied</title>
То сразу записывать ссылку из переменной inputLink в нужный фаил(и прекратить работу). А если никакие из условий не были выполнены, то тогда записывать ссылку из переменной inputLink в фаил unknown.txt.

Проще говоря я хочу сделать несколько условий:
- по expired доменам и если что то нашлось записывать в фаил Domain-Expired.txt
- по ссылкам с гео блоком в фаил Access-denied.txt
в будующем возможно будут ещё условия со своим фаилом

Может у кого то есть пример такого кода/сниппета ?

вот примерно как выглядит условия с Domain-Expired.txt Это код написанный chatgpt, а сами условия в содержаться внутри тега title
C#:
    { "<title>GreenGeeks Domain Landing Page</title>", "Domain-Expired.txt" },
    { "<title>Account Suspended</title>", "Domain-Expired.txt" },
    { "<title>GoDaddy Domain Name Search</title>", "Domain-Expired.txt" },
    { "<title>Доменное имя временно заблокировано.</title>", "Domain-Expired.txt" },
    { "<title>.xyz Domain Names | Join Generation XYZ</title>", "Domain-Expired.txt" },
    { "<title>STRATO - Domain not available</title>", "Domain-Expired.txt" },
    { "<title>Suspended Domain</title>", "Domain-Expired.txt" },
    { "<title>域名到期-域名续费提醒</title>", "Domain-Expired.txt" },
    { "<title>抱歉,站点已暂停</title>", "Domain-Expired.txt" },
    { "<title>Türkiye’nin Lider Domain & Hosting Markası | Natro</title>", "Domain-Expired.txt" },
 
Последнее редактирование:

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 794
Благодарностей
2 466
Баллы
113
lock (project) // Можно использовать любой объект для блокировки, например, project
Если бы речь была о том, что внутри потока Вы обрабатывали бы данные в несколько потоков - тогда такой вариант возможно мог бы подойти.
Но, если обработка происходит в отдельных потоках Зенно - то естественно, что объект project не статический, а значит каждый поток имеет свою локальную копию, которая видна исключительно внутри этого потока.
Таким образом, данная инструкция в данном конкретном случае не корректна.
Возможно, есть смысл использовать предусмотренный для этих целей объект или создать свой статический объект для данных целей.
C#:
lock(SyncObjects.ListSyncer)
 
  • Спасибо
Реакции: Akcium и zerosearcher

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Возможно, есть смысл использовать предусмотренный для этих целей объект или создать свой статический объект для данных целей.
попробовал заменить
C#:
lock (project) // Можно использовать любой объект для блокировки, например, project
на

C#:
lock(SyncObjects.ListSyncer)
к сожалению это не сработало
 

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 794
Благодарностей
2 466
Баллы
113
к сожалению это не сработало
Тогда создайте в общем коде новый статический класс, а в нем новый статический объект.
И уже его используйте для лока.

Кстати вот здесь:
Выполнение действия CSharp OwnCode: Parsing All stuff. Процесс не может получить доступ к файлу "E:\BASE\(другой софт)\BadUrlsSorter\files\output\Unknown.txt", так как этот файл используется другим процессом.
На сколько я понимаю речь о строках:

C#:
if (!matchFound)
{
    string defaultFilePath = Path.Combine(outputDirectory, "Unknown.txt");
    File.AppendAllText(defaultFilePath, project.Variables["inputLink"].Value + Environment.NewLine);
}
И как видим, лока здесь нет - из-за чего здесь также будет вываливаться ошибка.
 
  • Спасибо
Реакции: Akcium и zerosearcher

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Тогда создайте в общем коде новый статический класс, а в нем новый статический объект.
И уже его используйте для лока.
Я не знаю как это сделать, это мой первый шаблон на C# и с многопотоком я до этого не работал.


И как видим, лока здесь нет - из-за чего здесь также будет вываливаться ошибка.
Извиняюсь я уже столько разных вариантов перепробовал что походу скинул по ошибке код без лока.



Кто нибудь может помочь с написанием такого кода? Чтобы запись в фаилы была с участием механизма lock чтобы какой то один поток мог писать в фаил, а другие потоки ждали пока он запишет и разблокирует фаил на запись? И чтобы остальные условия тоже выполнялись. Я сегодня также весь день потратил на общение с chatgpt, но так и не нашёл нормального варианта. единственный вариант который я в данный момент могу сделать, это налепить тысячу кубиков обработки переменной splitted_body, но это же полнейший бред.

вот входные данные:
C#:
outputDir = E:\BASE\(другой софт)\BadUrlsSorter\files\output\
inputLink = https://ya.ru
splitted_body = тело ответа гет запроса

Нужно парсить содержимое splitted_body на разные условия.
Если хотябы одно условие выполняеться, то он просто записывает inputLink в фаил для конкретного условия(об это ниже), а если ни одно условие не выполнилось, то тогда записывает inputLink в фаил outputDirUnknown.txt



Пример первого списка условий:
C#:
<title>Access denied</title>
<title>Request Rejected</title>
<title>Connection denied by Geolocation</title>

если хотя бы одно условие выполняеться, то есть если оно присутствует в splitted_body тогда записывать inputLink в фаил outputDirAccess-denied.txt


Пример второго списка условий:
C#:
<h1>Not Found</h1>
<title>404 Not Found</title>

если хотя бы одно условие выполняеться, то есть если оно присутствует в splitted_body тогда записывать inputLink в фаил outputDirNot-Found.txt


Пример третьего списка условий:
C#:
<title>GoDaddy Domain Name Search</title>
<title>Доменное имя временно заблокировано.</title>

если хотя бы одно условие выполняеться, то есть если оно присутствует в splitted_body тогда записывать inputLink в фаил outputDirDomain-Expired.txt
 
Последнее редактирование:

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 794
Благодарностей
2 466
Баллы
113
Я не знаю как это сделать, это мой первый шаблон на C# и с многопотоком я до этого не работал.
Прочитайте материал и сделайте по образу и подобию: https://zenno.club/discussion/threads/osobennosti-ispolzovanija-standartnyx-lockov-dlja-mnogopotoka.44524/

Извиняюсь я уже столько разных вариантов перепробовал что походу скинул по ошибке код без лока.
Просто в это место также необходимо добавить лок этого будет достаточно, например:
C#:
lock(SyncObjects.ListSyncer) {
File.AppendAllText(defaultFilePath, project.Variables["inputLink"].Value + Environment.NewLine);
}
Кто нибудь может помочь с написанием такого кода?
Я бы это описал примерно так:
C#:
string outputDir = project.Directory; // @"E:\BASE\(другой софт)\BadUrlsSorter\files\output\";
string inputLink = "https://ya.ru";
string splitted_body = "тело ответа гет запроса";
string default_file = "Unknown.txt";
var items = new Dictionary<string, string>() {
    { "<title>Access denied</title>", "Access-denied.txt" },
    { "<title>Request Rejected</title>", "Access-denied.txt" },
    { "<title>Connection denied by Geolocation</title>", "Access-denied.txt" },
   
    { "<h1>Not Found</h1>", "Not-Found.txt" },
    { "<title>404 Not Found</title>", "Not-Found.txt" },

    { "<title>GoDaddy Domain Name Search</title>", "Domain-Expired.txt" },
    { "<title>Доменное имя временно заблокировано.</title>", "Domain-Expired.txt" }
};

string path = string.Empty;
foreach(var item in items) {
    if(splitted_body.Contains(item.Key)) {
        path = Path.Combine(outputDir, item.Value );
        break;
    }
}
if(string.IsNullOrEmpty(path)) path = Path.Combine(outputDir, default_file );

lock(SyncObjects.ListSyncer) { // подразумевается, что другие шаблоны не работают с этим файлом и он не открывается в момент работы блокнотом или чем-то ещё!!!
    File.AppendAllText(path, inputLink + Environment.NewLine);
}
 
  • Спасибо
Реакции: Akcium и zerosearcher

Чешир

Client
Регистрация
27.06.2014
Сообщения
1 642
Благодарностей
977
Баллы
113
Анализировать ифами на условия и записывать в разные списки (привязанные к файлам) в зависимости от результата.
Обычными кубиками. Зачем тут сишарп?
Простейший же шаб
 

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3
Я бы это описал примерно так:
Большое вам спасибо! Ваш код работает и выполняет все необходимые условия!
Наконец то эта проблема решена, без вас я бы точно не справился, однозначно плюс в карму.

Обязательно прочитаю, ещё раз большое спасибо!
 
  • Спасибо
Реакции: BAZAg

zerosearcher

Client
Регистрация
17.09.2022
Сообщения
15
Благодарностей
2
Баллы
3

Akcium

Client
Регистрация
16.12.2020
Сообщения
259
Благодарностей
126
Баллы
43
Большое вам спасибо! Ваш код работает и выполняет все необходимые условия!
Наконец то эта проблема решена, без вас я бы точно не справился, однозначно плюс в карму.


Обязательно прочитаю, ещё раз большое спасибо!
Так это @BAZAg, большой души человек, всегда поможет и подскажет)
 

Akcium

Client
Регистрация
16.12.2020
Сообщения
259
Благодарностей
126
Баллы
43
Я уже кучу его сообщений - ответы использовал в своих целях, мое почтение...
 
  • Спасибо
Реакции: zerosearcher и BAZAg

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)