И снова список в многопотоке. lock (SyncObjects.ListSyncer)

Marlboro

Client
Регистрация
01.10.2020
Сообщения
38
Благодарностей
3
Баллы
8
Пользуюсь в многопоточном проекте таким кодом:

C#:
// берем из переменной текст, который надо искать

var textContains = project.Variables["var"].Value;

// получаем список, в котором будем искать

var sourceList = project.Lists["List"];

// ищем в каждой строчке в списке

lock (SyncObjects.ListSyncer)

{

    for (int i = 0; i < sourceList.Count; i++)

    {

        // читаем строку из списка

        var str = sourceList[i];

        // проверяем содержание текста в строке, если есть совпадение, возвращаем "no"

        if (str.Contains(textContains))

            return "no";

            //return null; // если раскомментировать эту строку, а предыдущую закомментировать (или удалить), выход будет по красной ветке (как вариант)

    }

}

// если нет совпадений, добавляем строку в список

sourceList.Add(textContains);
N разных потоков парсят одну и ту же страницу, в ожидании появления на ней благоприятного события с интересующим значением var. Это по типу того, как несколько разных таксистов ждут клиента на пятачке. И вот клиент приходит (благоприятное событие), а ехать клиенту нужно на улицу Строителей (значение var).

Дальше идет проверка через этот код, не взял ли уже другой параллельный поток это значение в работу.

Если не взял (значения нет в List) - добавляем значение var в лист, после чего поток работает с этим значением дальше согласно проекту.
Если взял (уже есть значение в List) значит возвращаем "no" и просто ждем, когда появятся другие значения var для проверки.

Так вот, несмотря на то, что тут используется lock (SyncObjects.ListSyncer), иногда случается так, что два разных потока берут в работу одно и то же значение var.
Причем, согласно трассировке, происходит это почти в одно и то же время с разницей в доли секунды, причем не видно чтобы была какая-то задержка, которая как я понимаю должна быть, ведь список все-таки должен лочиться:

Вот 1-й поток проверяет:
04-05-2022 01:09:56.3927|Good|5d71afc5-6f4e-4c8c-8200-5180c4716687|0

А вот 2-й проверяет и тоже берет значение в работу:
04-05-2022 01:09:56.4978|Good|5d71afc5-6f4e-4c8c-8200-5180c4716687|0

В общем, хочу поинтересоваться, отчего такое может быть. Нормально ли вообще такое поведение?
 

djaga

Client
Регистрация
26.04.2020
Сообщения
505
Благодарностей
1 050
Баллы
93
Пользуюсь в многопоточном проекте таким кодом:

C#:
// берем из переменной текст, который надо искать

var textContains = project.Variables["var"].Value;

// получаем список, в котором будем искать

var sourceList = project.Lists["List"];

// ищем в каждой строчке в списке

lock (SyncObjects.ListSyncer)

{

    for (int i = 0; i < sourceList.Count; i++)

    {

        // читаем строку из списка

        var str = sourceList[i];

        // проверяем содержание текста в строке, если есть совпадение, возвращаем "no"

        if (str.Contains(textContains))

            return "no";

            //return null; // если раскомментировать эту строку, а предыдущую закомментировать (или удалить), выход будет по красной ветке (как вариант)
    }

}

// если нет совпадений, добавляем строку в список

sourceList.Add(textContains);
N разных потоков парсят одну и ту же страницу, в ожидании появления на ней благоприятного события с интересующим значением var. Это по типу того, как несколько разных таксистов ждут клиента на пятачке. И вот клиент приходит (благоприятное событие), а ехать клиенту нужно на улицу Строителей (значение var).

Дальше идет проверка через этот код, не взял ли уже другой параллельный поток это значение в работу.

Если не взял (значения нет в List) - добавляем значение var в лист, после чего поток работает с этим значением дальше согласно проекту.
Если взял (уже есть значение в List) значит возвращаем "no" и просто ждем, когда появятся другие значения var для проверки.

Так вот, несмотря на то, что тут используется lock (SyncObjects.ListSyncer), иногда случается так, что два разных потока берут в работу одно и то же значение var.
Причем, согласно трассировке, происходит это почти в одно и то же время с разницей в доли секунды, причем не видно чтобы была какая-то задержка, которая как я понимаю должна быть, ведь список все-таки должен лочиться:

Вот 1-й поток проверяет:
04-05-2022 01:09:56.3927|Good|5d71afc5-6f4e-4c8c-8200-5180c4716687|0

А вот 2-й проверяет и тоже берет значение в работу:
04-05-2022 01:09:56.4978|Good|5d71afc5-6f4e-4c8c-8200-5180c4716687|0

В общем, хочу поинтересоваться, отчего такое может быть. Нормально ли вообще такое поведение?
Не претендую на истину в последней инстанции, но возможно я вам помогу.)

Пометил что, изменил.
3‑я строчка — Так как SyncObjects.ListSyncer является устаревшим вариантом [указано]. Новый вариант FileSyncObjects.ListSyncer [пример].
5‑я строчка — Возможно можно было обойтись без этого, мне показалось так логичнее.
6‑я строчка — foreach делает то же самое, но короче.

9‑я строчка — str.Contains() по моему лучше заменить на проверку регулярным выражением Regex.IsMatch(input: " ", pattern: " "); // вернет bool.
C#:
string textContains = project.Variables["searchInList"].Value;

using (FileSyncObjects.ListSyncer.Lock())
{
    IZennoList sourceList = project.Lists["List"];
    foreach (var str in sourceList)
    {
        // проверяем содержание текста в строке, если есть совпадение, возвращаем "no"
        if (str.Contains(textContains))
            return "no";
    }
    // если нет совпадений, добавляем строку в список
    sourceList.Add(textContains);
    return "yes";
}
 
  • Спасибо
Реакции: Marlboro и kagorec

Marlboro

Client
Регистрация
01.10.2020
Сообщения
38
Благодарностей
3
Баллы
8
Большое спасибо за ответ! Заменил код на Ваш. Пару дней посмотрю как будет работать, отпишусь по результатам.
 

Marlboro

Client
Регистрация
01.10.2020
Сообщения
38
Благодарностей
3
Баллы
8
В общем, сам код работает отлично, но есть одна проблема. Прошу помочь)

В другом участке этого же шаблона у меня есть стандартный кубик, который удаляет из этого списка одно из значений: "Удалить строки, содержащие текст".

Как только происходит такое удаление, планово ожидается, что один из параллельных потоков снова "возьмет в работу" это значение с помощью Вашего кода. То есть убирая из списка значение, мы его как бы "уступаем" его для параллельного потока, который этого ждет.

И вот тут нежданчик: даже через Ваш код два разных потока берут в работу одно и то же значение, которое мы только что "уступили" через удаление из списка стандартным кубиком.

Подозреваю, что так происходит потому, что я неправильно удаляю значение из списка. Может быть, нужно тоже удалять его из списка через using FileSyncObjects.ListSyncer.Lock? Если я рассуждаю верно, подскажите, пожалуйста, правильный код как просто удалить значение из списка, используя этот лок.
 

djaga

Client
Регистрация
26.04.2020
Сообщения
505
Благодарностей
1 050
Баллы
93
В общем, сам код работает отлично, но есть одна проблема. Прошу помочь)

В другом участке этого же шаблона у меня есть стандартный кубик, который удаляет из этого списка одно из значений: "Удалить строки, содержащие текст".

Как только происходит такое удаление, планово ожидается, что один из параллельных потоков снова "возьмет в работу" это значение с помощью Вашего кода. То есть убирая из списка значение, мы его как бы "уступаем" его для параллельного потока, который этого ждет.

И вот тут нежданчик: даже через Ваш код два разных потока берут в работу одно и то же значение, которое мы только что "уступили" через удаление из списка стандартным кубиком.

Подозреваю, что так происходит потому, что я неправильно удаляю значение из списка. Может быть, нужно тоже удалять его из списка через using FileSyncObjects.ListSyncer.Lock? Если я рассуждаю верно, подскажите, пожалуйста, правильный код как просто удалить значение из списка, используя этот лок.
Если я правильно вас понял, вероятнее всего у вас не стоит галочка у списка "Сохранять изменения списка в файл"[gif ниже].
92220

Кубики если работают с файлами, то блокируют поток. Чтобы не было, что один и тот же поток взял одинаковое значение.
Если это не дало эффекта, напишите в личные сообщения [ссылка в подписи].
 

loshpek

Client
Регистрация
24.11.2016
Сообщения
83
Благодарностей
34
Баллы
18
Не претендую на истину в последней инстанции, но возможно я вам помогу.)

Пометил что, изменил.
3‑я строчка — Так как SyncObjects.ListSyncer является устаревшим вариантом [указано]. Новый вариант FileSyncObjects.ListSyncer [пример].
5‑я строчка — Возможно можно было обойтись без этого, мне показалось так логичнее.
6‑я строчка — foreach делает то же самое, но короче.

9‑я строчка — str.Contains() по моему лучше заменить на проверку регулярным выражением Regex.IsMatch(input: " ", pattern: " "); // вернет bool.
C#:
string textContains = project.Variables["searchInList"].Value;

using (FileSyncObjects.ListSyncer.Lock())
{
    IZennoList sourceList = project.Lists["List"];
    foreach (var str in sourceList)
    {
        // проверяем содержание текста в строке, если есть совпадение, возвращаем "no"
        if (str.Contains(textContains))
            return "no";
    }
    // если нет совпадений, добавляем строку в список
    sourceList.Add(textContains);
    return "yes";
}
Подскажите, есть ли разница если строка - IZennoList sourceList = project.Lists["List"]; находится за локом?
 

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