ASP.NET Singleton类值不一致

时间:2018-11-26 10:15:54

标签: c# asp.net-mvc singleton

我有一个特定的要求,即仅允许5个用户同时访问WebAPI(特定于.NET 4.5)(作为许可计划的一部分)。当达到5个并发限制时,webapi应该拒绝来自 ANY 用户的任何进一步尝试。

我创建了一个Singleton类来计算方法的访问次数,并在处理完成后减少计数器的数量,并检查count> 5。

    [HttpGet]
    [Route("GetOrderStatus/{id}")]
    public async Task<string> GetOrderStatus(int id)
    {
        GlobalState state = GlobalState.Instance;
        string result;

        if (state.OrderStatus > 5)
        {
            Log.Debug("INVALID - state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
            return await Task.FromResult("INVALID");
        }
        else
        {
            state.OrderStatus++;
            Log.Debug("state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
            result = DoGetOrder(id);
            state.OrderStatus--;
        }

        return await Task.FromResult(result);
    }

    private string DoGetOrder(int OrderTypeId)
    {
        Thread.Sleep(5000);
        return "OK_" + OrderTypeId.ToString();
    }

我的单例课程在下面。

public sealed class GlobalState
{
    private static readonly Lazy<GlobalState> lazy = new Lazy<GlobalState>(() => new GlobalState());

    public int OrderStatus { get; set; }
    public int PlaceOrder { get; set; }

    public static GlobalState Instance { get { return lazy.Value; } }
    private GlobalState()
    {

    }
}

单元测试。

   using Flurl.Http;

   [TestMethod]
    public void GetOrderStatus_Returns_Valid()
    {
        StringBuilder sb = new StringBuilder();
        int success = 0;
        int failed = 0;

        Parallel.For(0, 100, i =>
        {
            string url = "http://localhost:61803/api/getorderstatus/" + i.ToString();

            var responseString = url.GetStringAsync().Result;
            sb.AppendLine(responseString + ", ");

            if (responseString.Contains("INVALID"))
            {
                failed++;
            }
            else
            {
                success++;
            }
        });
    }

测试结果出错。日志复制如下。

2018-11-26 10:00:29.431 +00:00 [DBG] state.OrderStatus: 2, ID: 0
2018-11-26 10:00:29.432 +00:00 [DBG] state.OrderStatus: 3, ID: 25
2018-11-26 10:00:29.434 +00:00 [DBG] state.OrderStatus: 4, ID: 50
2018-11-26 10:00:30.310 +00:00 [DBG] state.OrderStatus: 5, ID: 75
2018-11-26 10:00:31.311 +00:00 [DBG] state.OrderStatus: 6, ID: 1
2018-11-26 10:00:32.313 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 26
2018-11-26 10:00:33.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 27
2018-11-26 10:00:33.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 51
2018-11-26 10:00:34.308 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 28
2018-11-26 10:00:34.308 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 52
2018-11-26 10:00:34.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 76
2018-11-26 10:00:35.309 +00:00 [DBG] state.OrderStatus: 4, ID: 3
2018-11-26 10:00:35.309 +00:00 [DBG] state.OrderStatus: 5, ID: 34
2018-11-26 10:00:35.309 +00:00 [DBG] state.OrderStatus: 4, ID: 30
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 5, ID: 54
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 5, ID: 79
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 6, ID: 77
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 6, ID: 53
2018-11-26 10:00:36.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 2
2018-11-26 10:00:36.321 +00:00 [DBG] state.OrderStatus: 6, ID: 29
2018-11-26 10:00:37.309 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 5
2018-11-26 10:00:37.309 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 56
2018-11-26 10:00:37.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 7
2018-11-26 10:00:38.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 8
2018-11-26 10:00:39.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 57
2018-11-26 10:00:39.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 6
2018-11-26 10:00:39.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 10
2018-11-26 10:00:39.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 81
2018-11-26 10:00:39.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 9
2018-11-26 10:00:40.355 +00:00 [DBG] state.OrderStatus: 4, ID: 14
2018-11-26 10:00:40.356 +00:00 [DBG] state.OrderStatus: 5, ID: 4
2018-11-26 10:00:40.357 +00:00 [DBG] state.OrderStatus: 6, ID: 35
2018-11-26 10:00:40.357 +00:00 [DBG] state.OrderStatus: 6, ID: 36
2018-11-26 10:00:40.357 +00:00 [DBG] state.OrderStatus: 6, ID: 58
2018-11-26 10:00:40.359 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 18
2018-11-26 10:00:42.310 +00:00 [DBG] state.OrderStatus: 3, ID: 78
2018-11-26 10:00:42.310 +00:00 [DBG] state.OrderStatus: 3, ID: 84
2018-11-26 10:00:42.310 +00:00 [DBG] state.OrderStatus: 2, ID: 59
2018-11-26 10:00:42.311 +00:00 [DBG] state.OrderStatus: 4, ID: 31
2018-11-26 10:00:42.311 +00:00 [DBG] state.OrderStatus: 4, ID: 55
2018-11-26 10:00:42.313 +00:00 [DBG] state.OrderStatus: 5, ID: 37
2018-11-26 10:00:42.313 +00:00 [DBG] state.OrderStatus: 6, ID: 80
2018-11-26 10:00:43.313 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 11
2018-11-26 10:00:44.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 20
2018-11-26 10:00:44.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 12
2018-11-26 10:00:45.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 60
2018-11-26 10:00:45.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 13
2018-11-26 10:00:45.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 21
2018-11-26 10:00:45.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 19
2018-11-26 10:00:45.318 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 82
2018-11-26 10:00:45.320 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 39
2018-11-26 10:00:46.313 +00:00 [DBG] state.OrderStatus: 3, ID: 46
2018-11-26 10:00:46.313 +00:00 [DBG] state.OrderStatus: 4, ID: 64
2018-11-26 10:00:46.313 +00:00 [DBG] state.OrderStatus: 3, ID: 69
2018-11-26 10:00:46.314 +00:00 [DBG] state.OrderStatus: 5, ID: 22
2018-11-26 10:00:46.315 +00:00 [DBG] state.OrderStatus: 6, ID: 71
2018-11-26 10:00:46.315 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 61
2018-11-26 10:00:46.315 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 24
2018-11-26 10:00:46.315 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 44
2018-11-26 10:00:46.317 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 15
2018-11-26 10:00:46.321 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 85
2018-11-26 10:00:46.324 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 62
2018-11-26 10:00:46.324 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 87
2018-11-26 10:00:46.325 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 45
2018-11-26 10:00:46.327 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 16
2018-11-26 10:00:46.331 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 88
2018-11-26 10:00:46.331 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 65
2018-11-26 10:00:46.333 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 83
2018-11-26 10:00:46.336 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 40
2018-11-26 10:00:46.338 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 86
2018-11-26 10:00:46.338 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 23
2018-11-26 10:00:46.343 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 66
2018-11-26 10:00:46.345 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 63
2018-11-26 10:00:46.345 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 89
2018-11-26 10:00:46.345 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 17
2018-11-26 10:00:46.351 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 41
2018-11-26 10:00:46.352 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 67
2018-11-26 10:00:46.353 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 90
2018-11-26 10:00:46.355 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 42
2018-11-26 10:00:46.359 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 43
2018-11-26 10:00:46.359 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 68
2018-11-26 10:00:46.362 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 91
2018-11-26 10:00:46.365 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 92
2018-11-26 10:00:46.368 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 93
2018-11-26 10:00:46.371 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 94
2018-11-26 10:00:46.374 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 95
2018-11-26 10:00:46.377 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 96
2018-11-26 10:00:46.380 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 97
2018-11-26 10:00:46.383 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 98
2018-11-26 10:00:46.386 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 99
2018-11-26 10:00:47.321 +00:00 [DBG] state.OrderStatus: 1, ID: 38
2018-11-26 10:00:47.321 +00:00 [DBG] state.OrderStatus: 1, ID: 32
2018-11-26 10:00:51.323 +00:00 [DBG] state.OrderStatus: -1, ID: 47
2018-11-26 10:00:51.323 +00:00 [DBG] state.OrderStatus: -2, ID: 72
2018-11-26 10:00:51.324 +00:00 [DBG] state.OrderStatus: 0, ID: 70
2018-11-26 10:00:52.326 +00:00 [DBG] state.OrderStatus: 0, ID: 33
2018-11-26 10:00:56.327 +00:00 [DBG] state.OrderStatus: -1, ID: 73
2018-11-26 10:00:56.331 +00:00 [DBG] state.OrderStatus: 0, ID: 48
2018-11-26 10:01:01.331 +00:00 [DBG] state.OrderStatus: -1, ID: 74
2018-11-26 10:01:01.335 +00:00 [DBG] state.OrderStatus: -1, ID: 49

为了成功和无效,state.OrderStatus值将变为“ 6”,并且在日志中也将变为负值,并且始终不一致。

请告知我我做错了。

1 个答案:

答案 0 :(得分:1)

这只是一个并发问题。可以通过多个线程将if (state.OrderStatus > 5)评估为false,然后它们都使用state.OrderStatus++;递增(它本身具有读写问题)。

解决方案是对此类脚本使用Interlocked.Increment(请注意,state.OrderStatus应该是一个字段,以便编译此代码,而不是属性):

int current = Interlocked.Increment(ref state.OrderStatus);

try
{
    if (current > 5)
    {
        Log.Debug("INVALID - state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
        return await Task.FromResult("INVALID");
    }
    else
    {
        Log.Debug("state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
        result = DoGetOrder(id);
    }
}
finally
{
    Interlocked.Decrement(ref state.OrderStatus);
}