關於單例及靜態變數測試

DaiWK發表於2024-08-19

服務端測試程式碼

靜態類

 public class StaticClass
 {
     public static int Count = 0;
     public static int SafeCount = 0;
     public static int GetCount()
     {
         return Count++;
     }

     private static readonly object lockObj = new object();
     public static int SafeGetCount()
     {
         lock (lockObj)
         {
             return SafeCount++;
         }
     }
 }

單例服務

 public class TestSerivceSingleton : ITestSerivceSingleton
 {
     public string Name { get; set; }
     public int Count { get; set; }
     private readonly ITestSerivceTransient testSerivceTransient;

     public TestSerivceSingleton(ITestSerivceTransient testSerivceTransient)
     {
         Name = Guid.NewGuid().ToString();
         this.testSerivceTransient = testSerivceTransient;
     }
     //public TestSerivceSingleton()
     //{   
     //      Name = Guid.NewGuid().ToString();
     //}
     public string GetServiceName()
     {
         return "TestSerivceSingleton" +Name;
     }

     public string GetServiceNameChild()
     {
         return this.testSerivceTransient.GetServiceName();
     }



     public int GetCount() { return Count++; }

     private readonly object lockObj = new object();
     public int SafeCount = 0;
     public int SafeGetCount()
     {

         lock (lockObj)
         {
             return SafeCount++;
         }
     }

 }

測試程式碼

[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
    private readonly ITestSerivceSingleton testSerivceSingleton;
    private readonly ITestSerivceScoped testSerivceScoped;
    private readonly ITestSerivceTransient testSerivceTransient;
    private readonly ITestSerivceSingleton testSerivceSingleton1;
    private readonly ITestSerivceScoped testSerivceScoped1;
    private readonly ITestSerivceTransient testSerivceTransient1;
    public HomeController(ITestSerivceSingleton testSerivceSingleton,ITestSerivceScoped testSerivceScoped,ITestSerivceTransient testSerivceTransient,
        ITestSerivceSingleton testSerivceSingleton1, ITestSerivceScoped testSerivceScoped1, ITestSerivceTransient testSerivceTransient1) { 
        this.testSerivceSingleton = testSerivceSingleton;
        this.testSerivceScoped = testSerivceScoped;
        this.testSerivceTransient = testSerivceTransient;
        this.testSerivceSingleton1 = testSerivceSingleton1;
        this.testSerivceScoped1 = testSerivceScoped1;
        this.testSerivceTransient1 = testSerivceTransient1;
    }

    [HttpGet("Index")]
    public IActionResult Index()
    {
       StringBuilder stringBuilder = new StringBuilder();
        #region MyRegion
        //stringBuilder.AppendLine(testSerivceSingleton.GetServiceNameBase());
        //stringBuilder.AppendLine(testSerivceSingleton.GetServiceName());
        //stringBuilder.AppendLine(testSerivceSingleton1.GetServiceName());
        //stringBuilder.AppendLine(testSerivceScoped.GetServiceNameBase());
        //stringBuilder.AppendLine(testSerivceScoped.GetServiceName());
        //stringBuilder.AppendLine(testSerivceScoped1.GetServiceName());
        //stringBuilder.AppendLine(testSerivceTransient.GetServiceNameBase());
        //stringBuilder.AppendLine(testSerivceTransient.GetServiceName());
        //stringBuilder.AppendLine(testSerivceTransient1.GetServiceName());

        //Transient作為Singleton成員
        //stringBuilder.AppendLine(testSerivceSingleton.GetServiceNameChild());
        //stringBuilder.AppendLine(testSerivceSingleton1.GetServiceNameChild());


        //Singleton作為Transient成員
        // stringBuilder.AppendLine(testSerivceTransient.GetServiceNameChild());
        // stringBuilder.AppendLine(testSerivceTransient1.GetServiceNameChild());


        //stringBuilder.AppendLine(testSerivceScoped.GetCount().ToString());
        //stringBuilder.AppendLine(testSerivceTransient.GetCount().ToString()); 
        #endregion
        stringBuilder.AppendLine($"非執行緒安全+單例:{testSerivceSingleton.GetCount().ToString()}");
        stringBuilder.AppendLine($"非執行緒安全+靜態變數:{StaticClass.GetCount().ToString()}");
        stringBuilder.AppendLine($"執行緒安全+單例:{testSerivceSingleton.SafeGetCount().ToString()}");
        stringBuilder.AppendLine($"執行緒安全+靜態變數:{StaticClass.SafeGetCount().ToString()}");



        return Content(stringBuilder.ToString());
    }
}

客戶端模擬http請求

#region 多執行緒下靜態變數/單例測試
#if true
await Task.Run(async () =>
{
int count = 100;
while (count >= 0)
{
Console.Clear();
await Task.Delay(100);
HttpClient httpClient = new HttpClient();
var response = await httpClient.GetAsync("http://localhost:5125/api/Home/Index");
if (response.StatusCode == HttpStatusCode.OK)
{
    var message = await response.Content.ReadAsStringAsync();
    Console.WriteLine(message);
}
count--;
}

});  
#endif
#endregion

啟動WEB服務,並開啟多個客戶端模擬併發請求

結論

1、無論是單例物件的成員變數還是靜態成員變數,所有請求都共用一個成員變數

2、在併發情況下,無論是單例物件的成員變數還是靜態成員變數都存線上程安全問題

相關文章