IIS和C#线程锁定

时间:2019-01-07 20:39:21

标签: c# multithreading

我对使用C#的线程和IIS有疑问, 我会感谢某人的帮助。

我不是一个熟练的C#程序员,所以请保持友好,我会 描述问题,然后我将展示我的代码, 最后我要问两个问题。

问题描述

我有一个Web API方法,该方法需要访问上的同一文件夹 磁盘上的每个请求,但每个调用都不应访问该文件夹 如果已经有另一个线程访问上述文件夹, 换句话说,一次只能有一个请求可以访问该文件夹。

代码片段原始版本(使用监视器)

我使用实体类“香蕉”(:P)举例说明了我的问题 每个请求都包含一个香蕉的ID,该ID用于 验证是否还有另一个请求正在访问 文件夹。

using System.Web.Hosting;
using System.Net.Http;
using System.Text;
using System.Threading;

[RoutePrefix("api/banana")]
public class BananaController
{
    private static readonly object bananasLocksAccess = new object();
    private static readonly Dictionary<long, object> bananasLocks = new Dictionary<long, object>();

    [Route("AccessBananaFolder")]
    [TokenAuthorize]
    [HttpGet]
    public BananaResponse AccessFolderWithBananaId(long bananaId)
    {
        BananaResponse r = new BananaResponse();
        Boolean bananaLockTaken = false;
        object bananaLocker = LockBanana(bananaId);
        try
        {
             System.Threading.Monitor.TryEnter(bananaLocker, ref bananaLockTaken);
             if (bananaLockTaken)
             {
                // safe code to access the folder of bananas
                // ...
                r = new BananaResponse("OK ACCESSING THE FOLDER");
             }
             else
             {
                r = new BananaResponse("COULD NOT ACCESS THE FOLDER RIGTH NOW, TRY AGAIN LATER");
             }
        }
        catch (Exception ex)
        {
            r = new BananaResponse("AN ERROR OCURRED ACCESSING THE FOLDER OF BANANAS");
        }
        finally
        {
            if (bananaLockTaken)
            {
                System.Threading.Monitor.Exit(bananaLocker);
            }
        }

        return r;
    }


    private static object LockBanana(long bananaId)
    {
        object bananaLocker;
        lock (bananasLocksAccess) {
            if (bananasLocks.ContainsKey(bananaId))
            {
                bananaLocker = bananasLocks[bananaId];
            }
            else
            {
                bananaLocker = new object();
                bananasLocks[bananaId] = bananaLocker;
            }
        }
        return bananaLocker;
    }
}

我的问题是

  • 1)这是解决C#问题的正确方法吗?

  • 2)由于IIS使用线程池管理请求/调用 (Is IISExpress Single-Threaded?),是否有两个不同请求访问该文件夹的更改? 因为是由池中的同一线程管理的?

非常感谢您!

================================================ ===========

更新!

代码片段版本2(使用Mutex)

@STW 的建议,我做了一个新版本 代码,但这次我使用Mutex来限制 对Set的访问,此set包含所有ID 当前正在访问文件夹的香蕉 香蕉(记住这是一个临时文件夹 每个香蕉ID),此新版本还改进了 @Remus Rusanu 提到的清理问题 主要问题。

    [RoutePrefix("api/banana")]
public class BananaController
{
    private static readonly Mutex bananasLocksAccess = new Mutex(false, "29c447fe-d146-4ea7-ac16-bb8e453ee065");
    private static readonly HashSet<long> bananasLocks = new HashSet<long>();

    [Route("AccessBananaFolder")]
    [TokenAuthorize]
    [HttpGet]
    public BananaResponse AccessFolderWithBananaId(long bananaId)
    {
        BananaResponse r = new BananaResponse();
        bool bananaLockTaken = false;
        try
        {
             bananaLockTaken = LockBanana(bananaId);
             if (bananaLockTaken)
             {
                // safe code to access the folder of bananas
                // ...
                r = new BananaResponse("OK ACCESSING THE FOLDER");
             }
             else
             {
                r = new BananaResponse("COULD NOT ACCESS THE FOLDER RIGTH NOW, TRY AGAIN LATER");
             }
        }
        catch (Exception ex)
        {
            r = new BananaResponse("AN ERROR OCURRED ACCESSING THE FOLDER OF BANANAS");
        }
        finally
        {
            if (bananaLockTaken)
            {
                UnlockBanana(bananaId);
            }
        }

        return r;
    }


    private static bool LockBanana(long bananaId)
    {
        bool lockTaken = false;
        try
        {
            lockTaken = bananasLocksAccess.WaitOne(0);
            if (lockTaken)
            {
                lockTaken = !bananasLocks.Contains(bananaId);
                if (lockTaken)
                {
                    bananasLocks.Add(bananaId);
                    lockTaken = true;
                }
            }
            bananasLocksAccess.ReleaseMutex();
        }
        catch (Exception e)
        {
            // could not complete the lock,  the user should try again later
        }
        return lockTaken;
    }

    private static void UnlockBanana(long bananaId)
    {
        try
        {
            bool lockTaken = bananasLocksAccess.WaitOne();
            if (lockTaken)
            {
                bananasLocks.Remove(bananaId);
            }
            bananasLocksAccess.ReleaseMutex();
        }
        catch (Exception e)
        {
            // the lock was released already
        }
    }

}

1 个答案:

答案 0 :(得分:0)

由于您的代码在IIS中执行,因此有可能跨多个进程运行代码的多个实例。  跨进程同步需要操作系统级别的锁定,而System.Threading.Mutex类是专门为此设计的。

Mutex有两种模式-本地命名。要进行跨进程工作,您将需要使用命名模式,对于这个名称,我建议对随机生成的GUID进行硬编码。