Sic.Framework-Nanjing-Baishi/Sicentury.Core/DeviceLocker.cs

178 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /************************************************************************
// * @file DeviceLocker.cs
// * @author Su Liang
// * @date 2022/11/16
// *
// * @copyright &copy Sicentury Inc.
// *
// * @brief 设备资源锁定器。
// *
// * @details 当某个设备或其它资源不能被多个线程同时使用时,可使用此锁定器实现资源互斥操作。
// *
// *
// * *****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Sicentury.Core
{
public class DeviceLocker
{
public enum Results
{
Ok,
Error,
IHaveLocker
}
private readonly object _syncRoot = new object();
/// <summary>
/// 返回占有该设备的对象名称.
/// <para>关于可能使用到的占用者名称,请参考枚举类型<see cref="ModuleName"/>。</para>
/// </summary>
private readonly List<string> _occupiersCollection;
private CancellationTokenSource _cts;
private SemaphoreSlim _semResource;
private readonly int _maxRes;
#region Constructor
public DeviceLocker(string lockerName, int maxResCount)
{
LockerName = lockerName;
_occupiersCollection = new List<string>();
_maxRes = maxResCount;
_semResource = new SemaphoreSlim(maxResCount, maxResCount);
}
#endregion
#region Properties
/// <summary>
/// 设备名称。
/// </summary>
public string LockerName { get; }
#endregion
#region MyRegion
/// <summary>
/// 锁定设备。
/// </summary>
/// <param name="occupier"></param>
/// <param name="ct"></param>
/// <param name="timeoutInMillisecond"></param>
/// <param name="reason"></param>
/// <returns></returns>
public Results TryLock(string occupier, out string reason, int timeoutInMillisecond)
{
lock (_syncRoot)
{
var a = _occupiersCollection.FirstOrDefault(x => x == occupier.ToString());
if (a != null)
{
reason = $"{LockerName} had been lock by {occupier}";
return Results.IHaveLocker;
}
if (_cts != null)
{
reason = $"{LockerName} is waiting to be locked";
return Results.Error;
}
_cts = new CancellationTokenSource();
}
try
{
if (_semResource.Wait(timeoutInMillisecond, _cts.Token))
{
_occupiersCollection.Add(occupier);
reason = "";
return Results.Ok;
}
reason =
$"Unable to lock {LockerName}, no enough resources, locked by {string.Join(", ", _occupiersCollection)}";
return Results.Error;
}
catch (OperationCanceledException)
{
reason = $"It's canceled to lock {LockerName}";
return Results.Error;
}
catch (Exception ex)
{
reason =
$"Unable to lock {LockerName}, {ex.Message}";
return Results.Error;
}
finally
{
// 无论锁定是否成功均销毁CancellationTokenSource。
_cts = null;
}
}
/// <summary>
/// 释放设备。
/// </summary>
/// <param name="occupier"></param>
/// <param name="reason"></param>
/// <returns></returns>
public Results TryUnlock(string occupier, out string reason)
{
lock (_syncRoot)
{
var a = _occupiersCollection.FirstOrDefault(x => x == occupier);
if (a == null)
{
reason = $"{LockerName} is never locked by {occupier}";
return Results.Error;
}
try
{
_semResource.Release(1);
_occupiersCollection.Remove(occupier);
reason = "";
return Results.Ok;
}
catch (Exception ex)
{
reason = $"Unable to lock {LockerName} by {occupier}, {ex.Message}";
return Results.Error;
}
}
}
/// <summary>
/// 取消等待可用的锁。
/// </summary>
public void CancelLockWaiting()
{
_cts?.Cancel();
}
public void Reset()
{
lock (_syncRoot)
{
_cts?.Cancel();
Thread.Sleep(100); // 等待 Semaphore Cancel
_semResource = new SemaphoreSlim(_maxRes, _maxRes);
_occupiersCollection.Clear();
}
}
#endregion
}
}