C#語法——泛型的多種應用 C#語法——await與async的正確打開方式 C#線程安全使用(五) C#語法——元組類型 好好耕耘 redis和memcached的區別

C#語法——泛型的多種應用

 

本篇文章主要介紹泛型的應用。html

泛型是.NET Framework 2.0 版類庫就已經提供的語法,主要用於提升代碼的可重用性、類型安全性和效率。redis

泛型的定義算法

下面定義了一個普通類和一個泛型類,咱們能夠明確看到泛型類和普通類最大的區別就是多了一個<T>。mongodb

因此,這個<T>就標記了,這個類是泛型類。其中這個T,也能夠寫成A,B,C,D或其餘字符。數據庫

1
2
3
4
public  class  Generic
{
     public  String Name;
}
1
2
3
4
public  class  Generic<T>
{
     public  T Name;
}

泛型,顧名思義,就是泛指的類型。比如男人,女人,白人,黑人,能夠泛稱爲【人】。緩存

但類型只能是一個類型。 那麼泛型和類型之間是什麼關係呢?安全

其實很簡單,泛型在定義的時候,是泛指類型;在使用的時候,就須要被指定,到底使用哪一個類型。網絡

即,使用時,就不在是泛指類型,而是特定類型。數據結構

比如,定義時,定義了一我的。但在使用時,必須明確指定,究竟是黑人仍是白人。併發

泛型的使用

泛型類跟普通類的使用方式同樣,都須要實例化對象,再由對象來調用內部的屬性或方法。

下面代碼實例化了泛型Generic,實例化時,還指定了該泛型Generic的指定類型爲String。

因此要給泛型Generic的屬性Name賦值,就須要賦值字符串類型的值。

1
2
3
4
5
public  static  void  Excute()
{
     Generic<String> gs =  new  Generic<String>();
     gs.Name =  "Kiba518" ;
}

下面代碼定義了一個Int類型的泛型Generic。

1
2
3
4
5
public  static  void  Excute()
{
     Generic< int > gs =  new  Generic< int >();
     gs.Name = 518;
}

泛型的默認值

泛型的默認值,以下面代碼所示。須要使用default(T)來賦值。

無論泛型究竟是String,int,bool或者是一個Class類型,均可以被自動賦值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  static  void  Excute()
{
     Generic< int > gs =  new  Generic< int >();
     gs.Name = 518;
     Generic<Task> gsTask =  new  Generic<Task>();
     gsTask.Name =  new  Task(()=> {
         Console.WriteLine( "Kiba518" );
     });
}
 
public  class  Generic<T>
{
     public  T Name =  default (T);
}

泛型的約束

在泛型類中,有個特別的約束可供咱們使用。

當咱們不顯示的聲明時,這個約束不存在。但當咱們顯示的聲明的時候,這個約束就會執行。

下面,咱們來看看這個特別的約束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  static  void  Excute()
{
     Generic<FanXing> gFanXing =  new  Generic<FanXing>();
     Generic<Base> gFanXingBase =  new  Generic<Base>();
     //Generic<string> gs = new Generic<string>(); 這樣定義會報錯
}
public  class  Generic<T>  where  T : Base
{
     public  T Name =  default (T);
}
public  class  Base 
{
     public  string  Name {  get set ; }
}
public  class  FanXing : Base
{
     public  new  string  Name {  get set ; }
}

如上面代碼所示,【where T : Base】就是這個特別的約束。

當顯示聲明這個約束的時候,定義會限制泛型的類型。

什麼是限制泛型的類型呢?

很簡單,泛型T,是泛指某一個類型。咱們在定義泛型類時,還需顯示的指定類型,此時咱們顯示指定的類型,要受這個限制。

這個限制就是指【where T : Base】。

它的限制是,要求咱們指定的類型T必須是Base,或者該類型繼承自Base,如FanXing類。

泛型的函數

在C#中,泛型不只能夠用於類,還能夠直接用於函數。

具體使用方式以下:

1
2
3
4
5
6
7
8
9
10
11
12
public  static  void  Excute()
{
     GenericFunc gf =  new  GenericFunc();
     gf.FanXingFunc<FanXing>( new  FanXing() { Name= "Kiba518" });
}
public  class  GenericFunc
{
     public  void  FanXingFunc<T>(T obj)
     {
         Console.WriteLine(obj.GetType());
     }
}

很簡單,調用泛型函數的時候,指定泛型函數的[指定類型]便可。

可是,這裏咱們發現一個問題,那就是,在泛型函數裏,使用泛型對象的時候,咱們發現對象都是object類型的。

那咱們若是想使用泛型對象裏的屬性和方法時,要怎麼辦呢?

也很簡單,反射就能夠了。

下面咱們添加一個反射函數GetPropertyValue,專門用來獲取屬性。

1
2
3
4
5
6
7
8
9
10
11
12
13
public  class  GenericFunc
{
     public  void  FanXingFunc<T>(T obj)
     {
         var  name = GetPropertyValue(obj,  "Name" );
         Console.WriteLine(name);
     }
     public  object  GetPropertyValue( object  obj,  string  name)
     {
         object  drv1 = obj.GetType().GetProperty(name).GetValue(obj,  null );
         return  drv1;
     }
}

輸出結果以下:

這樣咱們就獲得了咱們想要的結果,若是想使用泛型類裏的函數,道理也同樣,只須要用反射來調用便可。

結語

看到這裏,有些同窗可能會以爲泛型很複雜,連使用其對象下的屬性,都得反射,太繁瑣了,還不如不用呢。

有這樣想法的同窗,內心想一想就行了,若是對老司機這麼說,他確定會心裏默默的微笑,而後對你說,你想的沒錯。

而後,你就沒有而後了。

泛型的應用,開篇已經說了,主要用在提升代碼的可重用性、類型安全性和效率上。

若是隻是定義一個類,調用一個屬性,那泛型的存在就是雞肋。

但事實上,咱們的系統永遠只有更復雜,更復雜,更復雜。所以泛型纔有了用武之地。

 

----------------------------------------------------------------------------------------------------

注:此文章爲原創,歡迎轉載,請在文章頁面明顯位置給出此文連接!
若您以爲這篇文章還不錯,請點擊下右下角的【推薦】,很是感謝!

 

 

C#語法——await與async的正確打開方式

 

C#5.0推出了新語法,await與async,但相信你們仍是不多使用它們。關於await與async有不少文章講解,但有沒有這樣一種感受,你看完後,總感受這東西很不錯,但用的時候,老是想不起來,或者不知道該怎麼用。

爲何呢?我以爲你們的await與async的打開方式不正確。

 正確的打開方式

 
首先看下使用約束。

一、await 只能在標記了async的函數內使用。

二、await 等待的函數必須標記async。

有沒有感受這是個循環?沒錯,這就是個循環。這也就是爲何你們不怎麼用他們的緣由。這個循環很討厭,那麼怎麼破除這個循環呢?

【很簡單,await等待的是線程,不是函數。】

不理解嗎?不要緊,接着看下去。

下面從頭來說解,首先看這麼一組對比

1
2
3
4
5
6
7
8
public  static  int  NoAsyncTest()
{
    return  1;
}
public  static  async Task< int > AsyncTest()
{
   return  1;
}

 async Task<int>等於int

這意味着咱們在正常調用這兩個函數時,他們是等效的。那麼用async Task<int>來修飾int目的是什麼呢?

目的是爲了讓這個方法這樣被調用 await AsyncTest(),但直接這樣調用,並不會開啓線程,那這樣費勁的修飾是否是就沒什麼意義了呢。

固然不是,那何時會讓 await AsyncTest()有意義呢?

咱們接着往下看,修改AsyncTest以下。而後,此時再調用await AsyncTest(),你會神奇的發現,依然沒有卵用。。。

Excute方法正常執行,而AsyncTest內運行的線程,本身執行本身的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public  static  async  void  Excute()
  {
        Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 開始 Excute "  + DateTime.Now);
        await AsyncTest();
        Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 結束 Excute "  + DateTime.Now);
  }
 
  public  static  async Task< int > AsyncTest()
  {
         Task.Run(() =>
             {
                 Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run1 "  + DateTime.Now);
                 Thread.Sleep(1000);
             });
             return  1;
  }

彆着急,咱們稍做調整,在線程後面增長.GetAwaiter().GetResult()。這句話是幹什麼用的呢?是用來獲取線程返回值的。

這個邏輯是這樣的,若是想要獲取線程返回結果,就天然要等待線程結束。

運行一下,咱們將看下面的結果。

1
2
3
4
5
6
7
8
9
public  static  async Task< int > AsyncTest()
         {
             Task.Run(() =>
             {
                 Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run1 "  + DateTime.Now);
                 Thread.Sleep(1000);
             }).GetAwaiter().GetResult();
             return  1;
         }

 

可是,好像await AsyncTest();仍是沒啓做用。沒錯,事實就是,他真的不會起做用。。。

那麼怎麼才能讓他起做用呢?

首先,咱們定義一個普通函數,他的返回值是一個Task,而後咱們獲得Task後,運行它,再用await等待這個Task。

因而咱們就獲得這樣的結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  static  async  void  Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 開始 Excute "  + DateTime.Now);
            var  waitTask = AsyncTestRun();
            waitTask.Start();
            int  i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " i "  + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 結束 Excute "  + DateTime.Now);
        }
        public  static  Task< int > AsyncTestRun()
        {
            Task< int > t =  new  Task< int >(() => {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run1 "  + DateTime.Now);
                Thread.Sleep(1000);
                return  100;
            });
            return  t;
        }

  

如圖,這樣寫await AsyncTest();就起做用了。

因此,仍是那句話,await等待的是線程,不是函數。

但在圖裏,咱們發現很奇怪的一點,結束Excute也是線程3,而不是線程1。也就是說,Await會對線程進行優化。

下面看下兩組代碼的對比,讓咱們就更清楚的瞭解下Await。

第一組,使用await等待線程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public  static  async  void  Excute()
{
    Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 開始 Excute "  + DateTime.Now);
    await SingleAwait();
    Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 結束 Excute "  + DateTime.Now);
}
       
public  static  async Task SingleAwait()
{
      Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " AwaitTest開始 "  + DateTime.Now);
      await Task.Run(() =>
             {
                 Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run1 "  + DateTime.Now);
                 Thread.Sleep(1000);
             });
             await Task.Run(() =>
             {
                 Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run2 "  + DateTime.Now);
                 Thread.Sleep(1000);
             });
             Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " AwaitTest結束 "  + DateTime.Now);
             return ;
}

 

第二組,使用等待線程結果,等待線程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  static  async  void  Excute()
{
      Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 開始 Excute "  + DateTime.Now);
             await SingleNoAwait();
      Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " 結束 Excute "  + DateTime.Now);
         }
public  static  async Task SingleNoAwait()
{
       Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " SingleNoAwait開始 "  + DateTime.Now);
        Task.Run(() =>
         {
                 Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run1 "  + DateTime.Now);
                 Thread.Sleep(1000);
             }).GetAwaiter().GetResult();
             Task.Run(() =>
             {
                 Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " Run2 "  + DateTime.Now);
                 Thread.Sleep(1000);
             }).GetAwaiter().GetResult();
             Console.WriteLine(Thread.CurrentThread.GetHashCode() +  " SingleNoAwait結束 "  + DateTime.Now);
             return ;
}

能夠明確的看到,第二組,線程從新回到了主線程1中,而第一組,已經被優化到了線程4中。

 

 結語

await是一種很便捷的語法,他的確會讓代碼簡潔一些,但他主動優化線程的功能,若是不瞭解就使用,可能會致使一些奇怪的BUG發生。

這也是官方爲何只提供了await調用服務的例子,由於,在程序內調用,await仍是要了解後,再使用,才安全。

----------------------------------------------------------------------------------------------------

 

 

C#線程安全使用(五)

 
 CancellationToken的多種應用

這是線程安全的最後一篇了,主要介紹CancellationToken的多種應用。

1,ThreadPool直接啓動線程,傳遞CancellationToken。

2,Task啓動線程,傳遞CancellationToken。Task傳遞方式分爲兩種,一種經過Task的參數進行傳遞,另外一種經過向線程內傳遞對象的方式傳遞CancellationToken。

3,CancellationToken的回調函數應用。

話很少說,請看代碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class  Program
   {
       static  void  Main( string [] args)
       {
           Console.WriteLine( "當前線程{0},當前狀態{1}" , Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState);
           //使用線程池建立線程,而後取消線程
           CancelWithThreadPoolMiniSnippet();
       }
       static  CancellationTokenSource cts =  new  CancellationTokenSource();
       static  CancellationToken token = cts.Token;
       static  void  CancelWithThreadPoolMiniSnippet()
       {
           Console.WriteLine( "當前線程{0},當前狀態{1}" , Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState);
 
           #region 使用QueueUserWorkItem的構造函數,傳遞cts.Token,但我不喜歡這個模式 跟蹤不了狀態
           //ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), ctn);
           #endregion
 
           #region 使用傳遞參數的模式 傳遞CancellationToken,這裏的cts.Token是做爲Action的參數傳遞的
           //var action = new Action<object>(DoSomeWork);
           //Task t = new Task(action, ctn);
           //t.Start();
           //Console.WriteLine("開始,當前線程{0},當前狀態{1}", t.GetHashCode(), t.Status);
           #endregion
 
           #region 使用Task的構造函數,傳遞cts.Token,但CancellationTokenSource要弄成全局變量,不然方法找不到,就取消不了。
           //Task t = new Task(Work, cts.Token);
           //t.Start();
           #endregion
 
           #region 註冊回調函數,當CancellationTokenSource.Cancel()執行後,調用回調函數
           token.Register(CallBack,  true );   //註冊回調函數
           Task t =  new  Task(Work);
           t.Start();
           #endregion
 
           Thread.SpinWait(5000000);
           
           cts.Cancel();
           Console.WriteLine( "結束,當前線程{0},當前狀態{1}" , t.GetHashCode(), t.Status);
           Console.Read();
       }
 
      
       static  void  DoSomeWork( object  obj)
       {
           CancellationToken token = (CancellationToken)obj;
           for  ( int  i = 0; i < 100000; i++)
           {
               Console.WriteLine(i);
               // Simulating work.
               //Thread.SpinWait(5000000);
 
               if  (token.IsCancellationRequested)
               {
                  
                   break ;
               }
           }
       }
 
 
       static  void  Work()
       {
          
           for  ( int  i = 0; i < 100000; i++)
           {
               Console.WriteLine(i);
               if  (token.IsCancellationRequested)
               {
 
                   break ;
               }
           }
       }
 
       static  void  CallBack()
       {
           
           Console.WriteLine( "I'm call back!"    );
       }
   }

代碼內執行結果以下,該結果爲CancellationToken的回調函數應用:

到此NET Framework4.0裏的線程安全就都講完了。。。。。。。

雖然第一篇文章是2013年,雖然歷時近五年,但請相信我,代碼早在五年前就已經寫完啦。只是我一直一直一直沒配文字發出來。。。。。。

不過,也多是最近寫文字的能力有所提高,因此就完成了四和五。

否則這線程安全的文章可能還要拖。。。。。。。。哈哈

 後記

在NET Framework4.6裏,微軟提供了async和await語法,也是有關線程安全,我將會在新的語法相關文章裏講解async和await的用法。

 

----------------------------------------------------------------------------------------------------

 

C#語法——元組類型

 
 元組Tuple
 

  咱們如今使用的C#語法已經能夠知足平常的開發需求,但C#語法還在進行版本的更新,在創造更多更優秀的語義來讓咱們使用。這裏介紹一下C#5.0裏的提供的語法——元組。

  在C#中定義Tuple對象,轉到定義查看,咱們會看到以下代碼

 #region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\mscorlib.dll
 #endregion

  即該語法在.Net Framework4框架中已經能夠支持了。

  元組Tuple是一種數據結構,具備特定數量和元素序列。什麼意思呢?就是元組是能夠存貯多種類型的對象,能夠想象一下當一個函數擁有多個不一樣類型的返回值時,咱們除了定義了一個返回值之外,還要定義多個out或ref類型返回值才能解決這個需求;固然咱們也能夠定義一個對象保存多個返回值。但如今咱們多了一個解決方案,定義返回值爲一個元組,就解決了一切。

  元組Tuple是能夠存貯多種類型的數據的。NET Framework 直接支持具備 1 到 7 元素的元組。 此外,您能夠建立由嵌套中的元組對象的元組的八個或多個元素Rest屬性Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>對象。

  元組經常使用四種方法︰

  1,用來表示一組數據。 例如,一個元組能夠表示的數據庫記錄,而且其組件能夠表示每一個字段的記錄。

  2,若要提供輕鬆訪問和數據集的操做。

  3,out參數 (在 C# 中) 或ByRef參數 (在 Visual Basic 中)。

  4,若要將多個值傳遞給經過單個參數的方法。 例如,Thread.Start(Object)方法只有一個參數,容許你提供一個線程在啓動時執行的方法的值。若是你提供Tuple<T1, T2, T3>對象做爲方法自變量,則能夠提供有三個項的數據的線程的啓動例程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class  Program
    {
        static  void  Main( string [] args)
        {
            var  tuple =  new  Tuple< string int int int >(
                             "Kiba" , 00001, 00002,
                             00003);
 
            Console.WriteLine(tuple.Item1);
            Console.WriteLine(tuple.Item2);
            Console.WriteLine(tuple.Item3);
            Console.WriteLine(tuple.Item4);
 
            var  tupleCalss =  new  Tuple<A, B>(
                         new  A(),  new  B());
            Console.WriteLine(tupleCalss.Item1.Name);
            Console.WriteLine(tupleCalss.Item2.Name);
            Console.ReadKey();
        }
    }
    public  class  A
    {
        public  string  name =  "A" ;
 
        public  string  Name {  get  => name;  set  => name = value; }
    }
    public  class  B
    {
        public  string  Name =  "B" ;
    }<br>}<br><br><br>

輸出結果
Kiba
1
2
3
A
B


 

【PS:這裏使用的目標框架是.net framework 4.0 ,咱們能夠看到屬性的聲明以下,即4.0已經支持=>模式的屬性設置了。】

1
2
3
public  string  name =  "A" ;
 
public  string  Name {  get  => name;  set  => name = value; }

 

 

好好耕耘 redis和memcached的區別

 

觀點一:

一、Redis和Memcache都是將數據存放在內存中,都是內存數據庫。不過memcache還可用於緩存其餘東西,例如圖片、視頻等等;

二、Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲;

三、虛擬內存--Redis當物理內存用完時,能夠將一些好久沒用到的value 交換到磁盤;

四、過時策略--memcache在set時就指定,例如set key1 0 0 8,即永不過時。Redis能夠經過例如expire 設定,例如expire name 10;

五、分佈式--設定memcache集羣,利用magent作一主多從;redis能夠作一主多從。均可以一主一從;

六、存儲數據安全--memcache掛掉後,數據沒了;redis能夠按期保存到磁盤(持久化);

七、災難恢復--memcache掛掉後,數據不可恢復; redis數據丟失後能夠經過aof恢復;

八、Redis支持數據的備份,即master-slave模式的數據備份;

九、mongodb和memcached不是一個範疇內的東西。mongodb是文檔型的非關係型數據庫,其優點在於查詢功能比較強大,能存儲海量數據。mongodb和memcached不存在誰替換誰的問題。

觀點二:

Redis與Memcached的區別

 若是簡單地比較Redis與Memcached的區別,大多數都會獲得如下觀點:
1 Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲。
2 Redis支持數據的備份,即master-slave模式的數據備份。
3 Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用。

在Redis中,並非全部的數據都一直存儲在內存中的。這是和Memcached相比一個最大的區別(我我的是這麼認爲的)。

Redis 只會緩存全部的key的信息,若是Redis發現內存的使用量超過了某一個閥值,將觸發swap的操做,Redis根據「swappability = age*log(size_in_memory)」計算出哪些key對應的value須要swap到磁盤。而後再將這些key對應的value持久化到磁 盤中,同時在內存中清除。這種特性使得Redis能夠保持超過其機器自己內存大小的數據。固然,機器自己的內存必需要可以保持全部的key,畢竟這些數據 是不會進行swap操做的。

同時因爲Redis將內存中的數據swap到磁盤中的時候,提供服務的主線程和進行swap操做的子線程會共享這部份內存,因此若是更新須要swap的數據,Redis將阻塞這個操做,直到子線程完成swap操做後才能夠進行修改。

能夠參考使用Redis特有內存模型先後的狀況對比:

VM off: 300k keys, 4096 bytes values: 1.3G used
VM on: 300k keys, 4096 bytes values: 73M used
VM off: 1 million keys, 256 bytes values: 430.12M used
VM on: 1 million keys, 256 bytes values: 160.09M used
VM on: 1 million keys, values as large as you want, still: 160.09M used 


當 從Redis中讀取數據的時候,若是讀取的key對應的value不在內存中,那麼Redis就須要從swap文件中加載相應數據,而後再返回給請求方。 這裏就存在一個I/O線程池的問題。在默認的狀況下,Redis會出現阻塞,即完成全部的swap文件加載後纔會相應。這種策略在客戶端的數量較小,進行 批量操做的時候比較合適。可是若是將Redis應用在一個大型的網站應用程序中,這顯然是沒法知足大併發的狀況的。因此Redis運行咱們設置I/O線程 池的大小,對須要從swap文件中加載相應數據的讀取請求進行併發操做,減小阻塞的時間。

redis、memcache、mongoDB 對比
從如下幾個維度,對redis、memcache、mongoDB 作了對比,歡迎拍磚

一、性能
都比較高,性能對咱們來講應該都不是瓶頸
整體來說,TPS方面redis和memcache差很少,要大於mongodb


二、操做的便利性
memcache數據結構單一
redis豐富一些,數據操做方面,redis更好一些,較少的網絡IO次數
mongodb支持豐富的數據表達,索引,最相似關係型數據庫,支持的查詢語言很是豐富


三、內存空間的大小和數據量的大小
redis在2.0版本後增長了本身的VM特性,突破物理內存的限制;能夠對key value設置過時時間(相似memcache)
memcache能夠修改最大可用內存,採用LRU算法
mongoDB適合大數據量的存儲,依賴操做系統VM作內存管理,吃內存也比較厲害,服務不要和別的服務在一塊兒

四、可用性(單點問題)

對於單點問題,
redis,依賴客戶端來實現分佈式讀寫;主從複製時,每次從節點從新鏈接主節點都要依賴整個快照,無增量複製,因性能和效率問題,
因此單點問題比較複雜;不支持自動sharding,須要依賴程序設定一致hash 機制。
一種替代方案是,不用redis自己的複製機制,採用本身作主動複製(多份存儲),或者改爲增量複製的方式(須要本身實現),一致性問題和性能的權衡

Memcache自己沒有數據冗餘機制,也不必;對於故障預防,採用依賴成熟的hash或者環狀的算法,解決單點故障引發的抖動問題。

mongoDB支持master-slave,replicaset(內部採用paxos選舉算法,自動故障恢復),auto sharding機制,對客戶端屏蔽了故障轉移和切分機制。


五、可靠性(持久化)

對於數據持久化和數據恢復,

redis支持(快照、AOF):依賴快照進行持久化,aof加強了可靠性的同時,對性能有所影響

memcache不支持,一般用在作緩存,提高性能;

MongoDB從1.8版本開始採用binlog方式支持持久化的可靠性


六、數據一致性(事務支持)

Memcache 在併發場景下,用cas保證一致性

redis事務支持比較弱,只能保證事務中的每一個操做連續執行

mongoDB不支持事務


七、數據分析

mongoDB內置了數據分析的功能(mapreduce),其餘不支持


八、應用場景
redis:數據量較小的更性能操做和運算上

memcache:用於在動態系統中減小數據庫負載,提高性能;作緩存,提升性能(適合讀多寫少,對於數據量比較大,能夠採用sharding)

MongoDB:主要解決海量數據的訪問效率問題   

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正確打開方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一個能夠返回值的異步操做。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //將在線程池上運行的指定工做排隊,並返回該工做的任務句柄。
            {//建立了新線程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //錯誤    1    「正確打開方式.Program.Main(string[])」: 入口點不能用「async」修飾符標記
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute " + DateTime.Now);
            //AsyncTest();   //正常調用不會創線程
            //AsyncTest();  //出現三行 說明 建立了 一個線程 、、出現四行 說明 建立了 兩個個線程  五行
            //可是,好像await AsyncTest();仍是沒啓做用。沒錯,事實就是,他真的不會起做用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待線程結束 而後執行後面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest開始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待線程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            await Task.Run(() =>       //Await會對線程進行優化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest結束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait開始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult(); //用來獲取線程返回值的。這個邏輯是這樣的,若是想要獲取線程返回結果,就天然要等待線程結束。
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait結束 " + DateTime.Now);
            return;
        }

    }
}
相關文章
相關標籤/搜索