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 { getset; }
}
public class FanXing : Base
{
    public new string Name { getset; }
}

如上面代码所示,【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<stringintintint>(
                            "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;
        }

    }
}

 

posted @ 2018-07-18 23:36  ~雨落忧伤~  阅读(201)  评论(0编辑  收藏  举报