本篇文章主要介紹泛型的應用。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的多種應用。
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#語法——元組類型
咱們如今使用的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 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; } } }