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的多种应用。
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的区别
观点一:
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 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(); ; }); return t; } public static async Task<int> AsyncTest() { Task.Run(() => //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。 {//创建了新线程 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(); }).GetAwaiter().GetResult(); ; } 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(); }); await Task.Run(() => //Await会对线程进行优化。 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(); }); 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(); }).GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。 Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(); }).GetAwaiter().GetResult(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now); return; } } }
C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别的更多相关文章
- C#语法——await与async的正确打开方式
C#5.0推出了新语法,await与async,但相信大家还是很少使用它们.关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者 ...
- C#面试题(转载) SQL Server 数据库基础笔记分享(下) SQL Server 数据库基础笔记分享(上) Asp.Net MVC4中的全局过滤器 C#语法——泛型的多种应用
C#面试题(转载) 原文地址:100道C#面试题(.net开发人员必备) https://blog.csdn.net/u013519551/article/details/51220841 1. . ...
- C#语法——泛型的多种应用
本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了一个普通类和一个泛型类,我们可以 ...
- C#语法——元组类型
元组Tuple 我们现在使用的C#语法已经可以满足日常的开发需求,但C#语法还在进行版本的更新,在创造更多更优秀的语义来让我们使用.这里介绍一下C#5.0里的提供的语法——元组. 在C#中定义T ...
- C#核心语法-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)
泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...
- 从一知半解到揭晓Java高级语法—泛型
目录 前言 探讨 泛型解决了什么问题? 扩展 引入泛型 什么是泛型? 泛型类 泛型接口 泛型方法 类型擦除 擦除的问题 边界 通配符 上界通配符 下界通配符 通配符和向上转型 泛型约束 实践总结 泛型 ...
- Java学习笔记-基础语法Ⅷ-泛型、Map
泛型 泛型本质上是参数化类型,也就是说所操作的数据类型被指定为一个参数,即将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型,这种参数类型可以用在类.方法和接口中,分别为泛型类.泛型方法 ...
- 毕业设计 之 五 PHP语法学习笔记
毕业设计 之 四 PHP语法学习笔记 作者:20135216 平台:windows10 软件:XAMPP,DreamWeaver 说明:该笔记是对网站编程语言的详细学习 一.PHP基础 0. 关于环境 ...
- 【笔记】记一次.net语法await和async的异步编程实验与笔记。
1.实践代码全记录: using System; using System.Collections.Generic; using System.Diagnostics; using System.Li ...
随机推荐
- 色彩空间-- RGB\HSV
颜色空间 标签(空格分隔): 计算机视觉 颜色通常用三个独立的属性来描述,三个独立变量综合作用,自然就构成一个空间坐标,这就是颜色空间. RGB和CMY颜色模型都是面向硬件的,而HSV(Hue Sat ...
- jarvis OJ WEB题目writeup
0x00前言 发现一个很好的ctf平台,题目感觉很有趣,学习了一波并记录一下 https://www.jarvisoj.com 0x01 Port51 题目要求是用51端口去访问该网页,注意下,要用具 ...
- ContextRefreshedEvent事件使用注意事项(Spring)
0 概述ContextRefreshedEvent 事件会在Spring容器初始化完成会触发该事件.我们在实际工作也可以能会监听该事件去做一些事情,但是有时候使用不当也会带来一些问题. 1 防止重复触 ...
- Centos6.5部署Rsyslog-日志的存储方式及监测服务状态
1.以IP地址命名 在/etc/rsyslog.conf中加入如下配置,并做好备注.添加这三行配置之后,远程日志会被单独输出到一个以IP命名的日志文件中. #IP format by zhz at x ...
- Window通过zip安装并启动mariadb
下载解压后进入bin目录 使用mysql_install_db.exe工具:https://mariadb.com/kb/en/mariadb/mysql_install_dbexe/ 安装完成后,在 ...
- Win7系统 mstsc远程桌面连接失败,提示“您的凭据不工作” 或者“无法连接到远程计算机”的问题。
WIN7 mstsc远程桌面连接其他电脑,提示"您的凭据不工作xxxxxxx"的问题. 或者提示: 本机通过mstsc远程桌面连接服务器,我们按照下面的步骤来逐一排查: 本机配置以 ...
- JS中原始类型Null和Undefined
Undefined类型只有一个值,即undefined.当声明的变量还未被初始化时,变量的默认值为undefined.Null类型也只有一个值,即null.null用来表示尚未存在的对象,常用来表示函 ...
- day05函数和模块
一. (.format)的两种方法: 二.format_map方法 三.global函数内定义值,修改函数外定义的值 四.字典和list是可变变量,可以直接修改 五.可变参数(*args).参数不是 ...
- 深入理解JVM(4)——对象内存的分配策略
一.Java所承担的自动内存管理主要是针对对象内存的分配和回收. 二.在Java虚拟机的五块内存空间中,程序计数器.Java虚拟机栈.本地方法栈内存的分配和回收都具有确定性,一般在编译阶段就能确定需要 ...
- http理解
http是一种基与客户端和服务端的架构模式,通过一种可靠的连接(URL)来交换消息,是一个诶状态的请求/响应协议. http协议传输过程 client发送request到server,server接收 ...