服務端測試程式碼
靜態類
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、在併發情況下,無論是單例物件的成員變數還是靜態成員變數都存線上程安全問題