Взаимоблокировки dll библиотеки (C# Framework dll)

Коллеги, доброго времени суток!

Весь мой код написан на C# (Тело программы и вспомогательные библиотеки) В моем консольном приложении имеются библиотеки(шлюзов) к которым я периодически обращаюсь для выполнения тех или иных запросов к сторонним сервисам. Но выглядит что я столкнулся с взаимоблокировками и на тестовой среде никак не могу их воспроизвести для отладки. (на бою взаимоблокировки бывают чаще поэтому сейчас мне приходится realtime мониторить что у меня на бою) Может есть предложения как воспроизвести взаимоблокировку dll (написанной на C#).

Обычно к одной библиотеке обращается по 5 потоков порой до 20, но в какие-то моменты что-то идет не так и сегодня наблюдал по логам как выполнялся код, выполнялся в библиотеке и был внезапно остановлен по сути на пустом месте при присвоении данных переменной которая является локальной для метода в котором было присвоение.

Подскажите с чего начать отладку. (Может можно как то разблокировать библиотеку если обнаружил проблему)

Пример того как выбираю необходимую библиотеку и обращаюсь к её методу

IPlugin gateway = (from plugin in plugins.Gateways where plugin.HowCode == "need_how_code" select plugin).First().CreateNewInstance();
gateway.RunOperation(command, param); ///Вот тут начинаются проблемы

Пример того как загружаю библиотеки при инициализации программы в некую переменную plugins. (при параллельном выборе проблем не возникает!)

[ImportMany(typeof (IPlugin))]
private IPlugin[] plugins;
public void Load() 
{
    
    try
    {
        CompositionContainer compositionContainer = new CompositionContainer(
            new DirectoryCatalog(Path.Combine(Environment.CurrentDirectory, gatewayPath), "*.dll"), 
            new ExportProvider[0]);

        CompositionBatch batch1 = new CompositionBatch();
        batch1.AddPart(this);
        CompositionBatch batch2 = batch1;
        compositionContainer.Compose(batch2);
        string[] strArray = new string[plugins.Length];
        int index = 0;
        foreach (IPlugin plugin in plugins)
        {
            string how = plugin.How;
            strArray[index] = plugin.How;
            ++index;
        }
        Config.Hows = string.Join(",", strArray);
    }
    catch (ReflectionTypeLoadException ex)
    {
        string message = ex.Message + "\r\n";
        foreach (Exception loaderException in ex.LoaderExceptions)
            message = message + loaderException.Message + "\r\n";
        throw new Exception(message);
    }
    catch (NotImplementedException ex)
    {
        throw new Exception("Ошибка загрузки [для " + ex.Source + " " + ex.Message + "]");
    }
    catch (Exception ex)
    {
        throw new Exception("Ошибка загрузки шлюза. [" + ex.Message + "]");
    }
}

/// 
public void RunOperation(Command command, string param)
        {
            switch (command)
            {
                case Command.get_log: 
                    operation = new GetLog(param);
                    break;
                case Command.config_update: 
                    operation = new ConfigUpdate(param);
                    break;
                default:
                    throw new Exeption(Errors.UNKNOW_COMMAND);
            }
            operation.Start();
        }

Ответы (1 шт):

Автор решения: aepot

сегодня наблюдал по логам как выполнялся код, выполнялся в библиотеке и был внезапно остановлен по сути на пустом месте при присвоении данных переменной которая является локальной для метода в котором было присвоение

Выполнение синхронного кода по присвоению переменной не может вызвать или попасть в блокировку. Блокировка скорее всего где-то не там, куда вы смотрели, например в каком-то другом потоке.

Взаимоблокировка называется deadlock, по этому ключевому слову можно найти очень много всего почитать.

И чаще всего взаимоблокировка находится не отладкой, особенно если она не гарантированная, а ситуативная, а анализом кода.

Смотрите те места, где вы ставите lock, есть ли внутри операции, которые могут быть залочены другим локом. Будет ли гарантированный выход из лока, или есть выход условный "пока не произойдет что-то там".

Вообще в асинхронном коде lock - штука не всегда хорошая, потому что он "вешает" текущий поток, в некоторых случаях лучше делать блокировку асинхронной, например с помощью SemaphoreSlim.

using (SemaphoreSlim semaphore = new SemaphoreSlim(1)) // 1 - сколько потоков может войти в зону светофора одновременно
{
    List<Task> tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
    {
        tasks.Add(DoJobAsync(semaphore));
    }
    await Task.WhenAll(tasks);
}

Запускаю 10 задач параллельно и жду завершения всех

private async Task DoJobAsync(SemaphoreSlim semaphore)
{
    // некритичный код

    await semaphore.WaitAsync();
    try
    {
        // код критичной зоны, только одна задача может находиться здесь в один момент времени
    }
    finally
    {
        semaphore.Release();
    }

    // некритичный код
}

Плюс к нему еще можно CancellationToken прикрутить. И например при завершении работы приложения отменить все ожидающие задачи.

Так же, вы наверное уже в курсе, что внутри лока нельзя использовать асинхронный код. Внутри светофора - можно.

И наконец, если ничего не помогает, можно проверить, а не в синхронизации ли потоков дело

await Task.Run(() => gateway.RunOperation(command, param));

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

→ Ссылка