我对使用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
}
}
}
答案 0 :(得分:0)
由于您的代码在IIS中执行,因此有可能跨多个进程运行代码的多个实例。 跨进程同步需要操作系统级别的锁定,而System.Threading.Mutex类是专门为此设计的。
Mutex
有两种模式-本地和命名。要进行跨进程工作,您将需要使用命名模式,对于这个名称,我建议对随机生成的GUID进行硬编码。