Предотвращение дублирования пользовательских транзакций с помощью пользовательских блокировок?

1

У нас есть устаревшая среда ASP.NET 2.0, где каждое исполнение страницы аутентифицируется определенному пользователю, и поэтому у меня есть целое число, представляющее введенный в систему идентификатор пользователя.

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

Очевидно, я мог бы создать статический объект и сделать lock(myObject) {... } вокруг всего фрагмента кода, чтобы попытаться предотвратить некоторые из этих условий гонки. Но я не хочу создавать узкое место для всех... просто хочу, чтобы один и тот же зарегистрированный пользователь запускал код одновременно или почти одновременно.

Поэтому я собираюсь создать экземпляр объекта для каждого пользователя и сохранить его в кеше на основе их идентификатора пользователя. Затем я просматриваю этот объект, и если объект найден, я блокирую его. Если не найден, я сначала создаю/кеширую его, а затем блокирую на нем.

Имеет ли это смысл? Есть ли лучший способ добиться того, что мне нужно?

Что-то вроде этого - вот что я думаю:

public class MyClass
{
    private static object lockObject = new object(); // global locking object

    public void Page_Load()
    {
        string cachekey = "preventdupes:" + UserId.ToString();
        object userSpecificLock = null;

        // This part would synchronize among all requests, but should be quick
        // as it is simply trying to find out if a user-specific lock object 
        // exists, and if so, it gets it. Otherwise, it creates and stores it.

        lock (lockObject)
        {
            userSpecificLock = HttpRuntime.Cache.Get(cachekey);
            if (userSpecificLock == null)
            {
                userSpecificLock = new object();

                // Cache the locking object on a sliding 30 minute window
                HttpRuntime.Cache.Add(cachekey, userSpecificLock, null,
                    System.Web.Caching.Cache.NoAbsoluteExpiration, 
                    new TimeSpan(0, 30, 0), 
                    System.Web.Caching.CacheItemPriority.AboveNormal, null);
            }
        }

        // Now we have obtained an instance of an object specific to the user, 
        // and we'll lock the next block of code specifically to them.

        lock (userSpecificLock)
        {
            try
            {
                // Perform some operations to check our database to see if the
                // transaction already occurred for this user, and if not, 
                // perform the transaction, and then record it into our db.
            } 
            catch (Exception)
            {
                // Rollback anything our code has done up until this exception,
                // so that if the user tries again, it will work.
            }
        }
    }
}
  • 0
    Возможно, стоит рассмотреть возможность использования статического ConcurrentDictionary UserId для Lock object пар Lock object вместо прямого изменения HttpRuntime.Cache .
Теги:
race-condition
locking

1 ответ

2
Лучший ответ

Решение состоит в использовании мьютекса.

Mutex можно назвать, так что вы можете назвать свой идентификатор пользователя, и они работают для полного компьютера, поэтому они работают, если у вас много процессов в одном и том же пуле (веб-сад).

Подробнее читайте:
http://en.wikipedia.org/wiki/Mutual_exclusion
Asp.Net. Доступ к синхронизации (мьютекс)
http://www.dotnetperls.com/mutex
MSute Mutex с примером

Некоторые моменты

lock Блокировка работает только внутри одного и того же родительского потока, и вы можете использовать их только для синхронизированных статических переменных. Но также HttpRuntime.Cache представляет собой статическую память, то есть означает, что если у вас много процессов под одним и тем же пулом (веб-сад), у вас много разных переменных кэша.

Страница также автоматически синхронизируется сеансом. Поэтому, если вы отключили сеанс для этой страницы, то в мьютексе есть точка, если нет, все сеанс сессии блокирует page_load (с мьютексом), а мьютекс, который вы собираетесь разместить, не имеет никакого значения.

Некоторая ссылка о:
Сервер ASP.NET не обрабатывает страницы асинхронно
Является ли Session переменной потокобезопасной в цикле Parallel.For на странице ASP.Net
HttpContext.Current имеет значение null, когда в методе, вызванном из PageAsyncTask

  • 0
    Mutex объект, очень интересный. Вы также просто поразили меня мыслью о блокировке ASP.NET Page_Load с помощью сеанса. Я никогда не осознавал этого, и я работаю с ASP.NET в течение 10 лет. Хлоп.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню