日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

身边的设计模式(一):单例 与 RedisCacheManager

發布時間:2023/12/4 数据库 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 身边的设计模式(一):单例 与 RedisCacheManager 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,以后我會用23篇文章,來給大家講解設計模式,當然如果你看過我的項目,很多設計模式已經很會了,只是沒有注意到,我這里會講解一下,大家就會發現,如果你看懂了我的項目,其實已經至少學會了六種設計模式了。

?一、什么是單例模式

【單例模式】,英文名稱:Singleton Pattern,這個模式很簡單,一個類型只需要一個實例,他是屬于創建類型的一種常用的軟件設計模式。通過單例模式的方法創建的類在當前進程中只有一個實例(根據需要,也有可能一個線程中屬于單例,如:僅線程上下文內使用同一個實例)。

  • 1、單例類只能有一個實例。

  • 2、單例類必須自己創建自己的唯一實例。

  • 3、單例類必須給所有其他對象提供這一實例。

那咱們大概知道了,其實說白了,就是我們整個項目周期內,只會有一個實例,當項目停止的時候,實例銷毀,當重新啟動的時候,我們的實例又會產品。

上文中說到了一個名詞【創建類型】的設計模式,那什么是創建類型的設計模式呢?

創建型(Creational)模式:負責對象創建,我們使用這個模式,就是為了創建我們需要的對象實例的。

那除了創建型還有其他兩種類型的模式:

? -結構型(Structural)模式:處理類與對象間的組合

??-行為型(Behavioral)模式:類與對象交互中的職責分

這兩種設計模式,以后會慢慢說到,這里先按下不表。

咱們就重點從0開始分析分析如何創建一個單例模式的對象實例。

?二、如何創建單例模式

實現單例模式有很多方法:從“懶漢式”到“餓漢式”,最后“雙檢鎖”模式。

這里咱們就慢慢的,從一步一步的開始講解如何創建單例,既然要創建單一的實例,那我們首先需要學會如何去創建一個實例,這個很簡單,相信每個人都會創建實例,就比如說這樣的:

/// <summary> /// 定義一個天氣類 /// </summary> public class WeatherForecast { public DateTime Date { get; set; } = DateTime.Now; public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } } [HttpGet] public WeatherForecast Get() { // 實例化一個對象實例 WeatherForecast weather = new WeatherForecast(); return weather; }

我們每次訪問的時候,時間都是會變化,所以我們的實例也是一直在創建,在變化,

相信每個人都能看到這個代碼是什么意思,不多說,直接往下走,我們知道,單例模式的核心目的就是:

必須保證這個實例在整個系統的運行周期內是唯一的,這樣可以保證中間不會出現問題。

那好,我們改進改進,不是說要唯一一個么,好說!我直接返回不就行了:

/// <summary> /// 定義一個天氣類 /// </summary> public class WeatherForecast { // 定義一個靜態變量來保存類的唯一實例 private static WeatherForecast uniqueInstance; // 定義私有構造函數,使外界不能創建該類實例 private WeatherForecast() { } /// <summary> /// 靜態方法,來返回唯一實例 /// 如果存在,則返回 /// </summary> /// <returns></returns> public static WeatherForecast GetInstance() { // 如果類的實例不存在則創建,否則直接返回 //?其實嚴格意義上來說,這個不屬于【單例】 if (uniqueInstance == null) { uniqueInstance = new WeatherForecast(); } return uniqueInstance; } public DateTime Date { get; set; } = DateTime.Now; public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } }

然后我們修改一下調用方法,因為我們的默認構造函數已經私有化了,不允許再創建實例了,所以我們直接這么調用:

[HttpGet] public WeatherForecast Get() { // 實例化一個對象實例 WeatherForecast weather = WeatherForecast.GetInstance(); return weather; }

最后來看看效果:

這個時候,我們可以看到,時間已經不發生變化了,也就是說我們的實例是唯一的了,大功告成!是不是很開心!

但是,別著急,問題來了,我們目前是單線程的,所以只有一個,那如果多線程呢,如果多個線程同時訪問,會不會也會正常呢?

這里我們做一個測試,我們在項目啟動的時候,用多線程去調用:

public WeatherForecast Get() { // 實例化一個對象實例 //WeatherForecast?weather?=?WeatherForecast.GetInstance(); // 多線程去調用 for (int i = 0; i < 3; i++) { var th = new Thread( new ParameterizedThreadStart((state) => { WriteWeather(); }) ); th.Start(i); } return null; }

然后我們看看效果是怎樣的,按照我們的思路,應該是只會走一遍構造函數,其實不是:

3個線程在第一次訪問GetInstance方法時,同時判斷(uniqueInstance ==null)這個條件時都返回真,然后都去創建了實例,這個肯定是不對的。那怎么辦呢,只要讓GetInstance方法只運行一個線程運行就好了,我們可以加一個鎖來控制他,代碼如下:

public class WeatherForecast { // 定義一個靜態變量來保存類的唯一實例 private static WeatherForecast uniqueInstance; // 定義一個鎖,防止多線程 private static readonly object locker = new object(); // 定義私有構造函數,使外界不能創建該類實例 private WeatherForecast() { } /// <summary> /// 靜態方法,來返回唯一實例 /// 如果存在,則返回 /// </summary> /// <returns></returns> public static WeatherForecast GetInstance() { // 當第一個線程執行的時候,會對locker對象 "加鎖", // 當其他線程執行的時候,會等待 locker 執行完解鎖 lock?(locker) { //?如果類的實例不存在則創建,否則直接返回 if?(uniqueInstance?==?null) { uniqueInstance?=?new?WeatherForecast(); } } return uniqueInstance; } public DateTime Date { get; set; } = DateTime.Now; public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } }

這個時候,我們再并發測試,發現已經都一樣了,這樣就達到了我們想要的效果,但是這樣真的是最完美的么,其實不是的,因為我們加鎖,只是第一次判斷是否為空,如果創建好了以后,以后就不用去管這個 lock 鎖了,我們只關心的是?uniqueInstance?是否為空,那我們再完善一下:

/// <summary> /// 定義一個天氣類 /// </summary> public class WeatherForecast { // 定義一個靜態變量來保存類的唯一實例 private static WeatherForecast uniqueInstance; // 定義一個鎖,防止多線程 private static readonly object locker = new object(); // 定義私有構造函數,使外界不能創建該類實例 private WeatherForecast() { } /// <summary> /// 靜態方法,來返回唯一實例 /// 如果存在,則返回 /// </summary> /// <returns></returns> public static WeatherForecast GetInstance() { // 當第一個線程執行的時候,會對locker對象 "加鎖", // 當其他線程執行的時候,會等待 locker 執行完解鎖 if (uniqueInstance == null) { lock (locker) { // 如果類的實例不存在則創建,否則直接返回 if (uniqueInstance == null) { uniqueInstance = new WeatherForecast(); } } } return uniqueInstance; } public DateTime Date { get; set; } = DateTime.Now; public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } }

這樣才最終的完美實現我們的單例模式!搞定。

?三、我們在哪里遇到過?

如果你看過我的 Blog.Core 項目的話,肯定看到過 Redis 那部分,我在那里就是封裝了一個單例,感興趣的可以看看:

public class RedisCacheManager : IRedisCacheManager { private readonly string redisConnenctionString; public volatile ConnectionMultiplexer redisConnection; private readonly object redisConnectionLock = new object(); public RedisCacheManager() { string redisConfiguration = Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "ConnectionString" });//獲取連接字符串 if (string.IsNullOrWhiteSpace(redisConfiguration)) { throw new ArgumentException("redis config is empty", nameof(redisConfiguration)); } this.redisConnenctionString = redisConfiguration; this.redisConnection = GetRedisConnection(); } /// <summary> /// 核心代碼,獲取連接實例 /// 通過雙if 夾lock的方式,實現單例模式 /// </summary> /// <returns></returns> private ConnectionMultiplexer GetRedisConnection() { //如果已經連接實例,直接返回 if (this.redisConnection != null && this.redisConnection.IsConnected) { return this.redisConnection; } //加鎖,防止異步編程中,出現單例無效的問題 lock (redisConnectionLock) { if (this.redisConnection != null) { //釋放redis連接 this.redisConnection.Dispose(); } try { this.redisConnection = ConnectionMultiplexer.Connect(redisConnenctionString); } catch (Exception) { //throw new Exception("Redis服務未啟用,請開啟該服務,并且請注意端口號,本項目使用的的6319,而且我的是沒有設置密碼。"); } } return this.redisConnection; } /// <summary> /// 清除 /// </summary> public void Clear() { foreach (var endPoint in this.GetRedisConnection().GetEndPoints()) { var server = this.GetRedisConnection().GetServer(endPoint); foreach (var key in server.Keys()) { redisConnection.GetDatabase().KeyDelete(key); } } } /// <summary> /// 判斷是否存在 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Get(string key) { return redisConnection.GetDatabase().KeyExists(key); } /// <summary> /// 查詢 /// </summary> /// <param name="key"></param> /// <returns></returns> public string GetValue(string key) { return redisConnection.GetDatabase().StringGet(key); } /// <summary> /// 獲取 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="key"></param> /// <returns></returns> public TEntity Get<TEntity>(string key) { var value = redisConnection.GetDatabase().StringGet(key); if (value.HasValue) { //需要用的反序列化,將Redis存儲的Byte[],進行反序列化 return SerializeHelper.Deserialize<TEntity>(value); } else { return default(TEntity); } } /// <summary> /// 移除 /// </summary> /// <param name="key"></param> public void Remove(string key) { redisConnection.GetDatabase().KeyDelete(key); } /// <summary> /// 設置 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="cacheTime"></param> public void Set(string key, object value, TimeSpan cacheTime) { if (value != null) { //序列化,將object值生成RedisValue redisConnection.GetDatabase().StringSet(key, SerializeHelper.Serialize(value), cacheTime); } } /// <summary> /// 增加/修改 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public bool SetValue(string key, byte[] value) { return redisConnection.GetDatabase().StringSet(key, value, TimeSpan.FromSeconds(120)); } }

好啦,今天的設計模式你學會了么,在我的項目中,還隱藏了,工廠模式,裝飾器模式,中介者模式,觀察者模式,享元模式等等等等,下次再見咯。

總結

以上是生活随笔為你收集整理的身边的设计模式(一):单例 与 RedisCacheManager的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。