C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别...

C#语法——泛型的多种应用

 

本篇文章主要介绍泛型的应用。

泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性、类型安全性和效率。

泛型的定义

下面定义了一个普通类和一个泛型类,我们可以明确看到泛型类和普通类最大的区别就是多了一个<T>。

所以,这个<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的打开方式不正确。

 正确的打开方式

 
首先看下使用约束。

1、await 只能在标记了async的函数内使用。

2、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的区别

 

观点一:

1、Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等;

2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储;

3、虚拟内存--Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘;

4、过期策略--memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10;

5、分布式--设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从;

6、存储数据安全--memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化);

7、灾难恢复--memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复;

8、Redis支持数据的备份,即master-slave模式的数据备份;

9、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 做了对比,欢迎拍砖

1、性能
都比较高,性能对我们来说应该都不是瓶颈
总体来讲,TPS方面redis和memcache差不多,要大于mongodb


2、操作的便利性
memcache数据结构单一
redis丰富一些,数据操作方面,redis更好一些,较少的网络IO次数
mongodb支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富


3、内存空间的大小和数据量的大小
redis在2.0版本后增加了自己的VM特性,突破物理内存的限制;可以对key value设置过期时间(类似memcache)
memcache可以修改最大可用内存,采用LRU算法
mongoDB适合大数据量的存储,依赖操作系统VM做内存管理,吃内存也比较厉害,服务不要和别的服务在一起

4、可用性(单点问题)

对于单点问题,
redis,依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制,因性能和效率问题,
所以单点问题比较复杂;不支持自动sharding,需要依赖程序设定一致hash 机制。
一种替代方案是,不用redis本身的复制机制,采用自己做主动复制(多份存储),或者改成增量复制的方式(需要自己实现),一致性问题和性能的权衡

Memcache本身没有数据冗余机制,也没必要;对于故障预防,采用依赖成熟的hash或者环状的算法,解决单点故障引起的抖动问题。

mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制。


5、可靠性(持久化)

对于数据持久化和数据恢复,

redis支持(快照、AOF):依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响

memcache不支持,通常用在做缓存,提升性能;

MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性


6、数据一致性(事务支持)

Memcache 在并发场景下,用cas保证一致性

redis事务支持比较弱,只能保证事务中的每个操作连续执行

mongoDB不支持事务


7、数据分析

mongoDB内置了数据分析的功能(mapreduce),其他不支持


8、应用场景
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;
        }

    }
}

 

转载于:https://www.cnblogs.com/cjm123/p/9333346.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值