使用特定于用户的锁防止重复的用户事务?

时间:2014-08-19 18:09:49

标签: c# asp.net locking race-condition

我们有一个遗留的ASP.NET 2.0环境,每个页面执行都会对特定用户进行身份验证,因此我有一个表示登录用户ID的整数。

在其中一个页面上,我需要运行一些代码,以防止用户执行重复操作。即使我们正在进行基本的防止欺诈检查,但发现很难保证这种情况不会发生。

显然,我可以创建一个静态对象并围绕整个代码执行lock(myObject) { ... }以尝试帮助防止这些竞争条件。但我不想为每个人创造瓶颈......只是想让同一个登录用户同时或几乎同时运行代码。

所以我想为每个用户创建一个对象实例,并根据用户ID将其存储在缓存中。然后我查找该对象,如果找到该对象,我会锁定它。如果没有找到,我首先创建/缓存它,然后锁定它。

这有意义吗?有没有更好的方法来完成我之后的事情?

这样的事情就是我在想的:

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.
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

解决方案是使用mutex

可以命名

Mutex ,因此您可以命名您的用户ID,并且它们适用于整个计算机,因此如果您在同一个池(Web园林)下有许多进程,它们就可以正常工作。

更多内容:
http://en.wikipedia.org/wiki/Mutual_exclusion
Asp.Net. Synchronization access(mutex)
http://www.dotnetperls.com/mutex
MSDN Mutex with example

一些要点

lock锁仅在相同和父线程内部工作,您只能将它们用于同步静态变量。但是HttpRuntime.Cache也是一个静态内存,这意味着如果你在同一个池(web园)下有很多进程,那么你有许多不同的Cache变量。

该会话也会自动同步该页面。因此,如果您已禁用此页面的会话,那么互斥锁有一个点,如果没有,会话就会锁定page_load(使用互斥锁),而您将要放置的互斥锁没有任何意义。

一些参考:
ASP.NET Server does not process pages asynchronously
Is Session variable thread-safe within a Parallel.For loop in ASP.Net page
HttpContext.Current is null when in method called from PageAsyncTask