Предотвратите выполнение кода пользователя веб-сокета дважды - PullRequest
3 голосов
/ 05 июня 2019

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

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

private readonly ConcurrentDictionary<string, bool> _inExecution = new ConcurrentDictionary<string, bool>();

private void SubscribeToSocket()
{
    _socket.Connect();

    var subscription = SocketSubscriptions.CreateSubsription(data =>
    {
        Task.Run(async () =>
        {
            // read data

            if (condition)
            {
                // call method only once
                await Execute(key);

                condition = false;
            }
        }
    }
}

private async Task Execute(string key)
{
    // Even with this statement the code calls are too fast and sometimes gets executed twice
    if (!_inExecution[key])
    {
        _inExecution[key] = true;

        // do something..
    }
}

Я уже пытался предотвратить двойное выполнение со случайным ожиданием перед методом Execute ().Вот так:

if (condition)
{
    var rnd = new Random();
    await Task.Delay(rnd.Next(15, 115));

    // call method only once
    await Execute(key);

    condition = false;
}

Но даже это выполнялось дважды в некоторых особых случаях.Есть ли лучший способ предотвратить это?

1 Ответ

1 голос
/ 06 июня 2019

Условие ключевой гонки здесь представляется гонкой между проверкой _inExecution[key] и * обновлением _inExecution [key] = true ';несколько абонентов могут пройти там.Есть несколько способов сделать это надежным, но в вашем случае , после рассмотрения я уверен, что самый простой подход - просто синхронизировать коллекцию, то есть

    private readonly HashSet<string> _inExecution = new HashSet<string>();
    private async Task Execute(string key)
    {
        // Even with this statement the code calls are too fast and sometimes gets executed twice
        bool haveLock = false;
        try
        {
            lock(_inExecution) { haveLock = _inExecution.Add(key); }
            if (haveLock)
            {
                // ... your code here
            }
        }
        finally
        {
            if (haveLock)
            {
                lock (_inExecution) _inExecution.Remove(key);
            }
        }
    }

Вы также можете использовать Dictionary<string, bool>, но HashSet<string> отлично работает здесь.Dictionary<string, bool> может избежать некоторых издержек пространства клавиш - просто манипулируя значением вместо этого - что-то вроде:

    private readonly Dictionary<string, bool> _inExecution = new Dictionary<string, bool>();
    private async Task Execute(string key)
    {
        // Even with this statement the code calls are too fast and sometimes gets executed twice
        bool haveLock = false;
        try
        {
            lock(_inExecution)
            {
                if (!_inExecution.TryGetValue(key, out var state) || !state)
                {   // if missing entirely, or not currently held: take it
                    haveLock = _inExecution[key] = true;
                }
            }
            if (haveLock)
            {
                // ... your code here
            }
        }
        finally
        {
            if (haveLock)
            {
                lock (_inExecution) _inExecution[key] = false;
            }
        }
    }

Важно отметить, что вы не держите lock над фактическим // ... your code here бит - это предотвратит все одновременные выполнения, а это не то, что вам нужно.

Если вы хотите привести в порядок, есть способы структурировать это с помощью пользовательских одноразовых изделий и т. Д., Но try / finally отлично работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...