为何须要异步,异步对可能起阻止做用的活动(例如,应用程序访问 Web 时)相当重要。 对 Web 资源的访问有时很慢或会延迟。 若是此类活动在同步过程当中受阻,则整个应用程序必须等待。 在异步过程当中,应用程序可继续执行不依赖 Web 资源的其余工做,直至潜在阻止任务完成。html
本节将一步一步带领你们理解async和await。web
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
static
void
Main(
string
[] args)
{
new
Thread(Test) { IsBackground =
false
}.Start();
//.Net 在1.0的时候,就已经提供最基本的API.
ThreadPool.QueueUserWorkItem(o => Test());
//线程池中取空闲线程执行委托(方法)
Task.Run((Action)Test);
//.Net 4.0以上可用
Console.WriteLine(
"Main Thread"
);
Console.ReadLine();
}
static
void
Test()
{
Thread.Sleep(1000);
Console.WriteLine(
"Hello World"
);
}
|
其实无论是Task,ThreadPool,本质最终都是Thread。只不过微软帮咱们在简化线程控制的复杂度。编程
线程池是CLR中事先定义好的一些线程。Task取的线程池,只不过在语法上,能够很是方便取返回值。安全
多线程会提升程序的效率,不会提升运行速度。多线程
这就比如这一个任务让前台花1个小时。前台完成10分钟的时候app
打电话给经理,让他安排一我的来干30分钟(new Thread()),他干剩下的20分钟。(建立线程,须要时间,内存资源)异步
或者从旁边空闲的同事中(ThreadPool 或 Task),拉一我的过来干30分钟。他干剩下的20分钟。(须要的时间少,资源原本就存在)async
从上看出,异步会让一份任务时间变长。资源消耗更多。可是可让前台(UI线程)空闲下来,遵从领导(用户)指挥。异步编程
首先看个Demo,post
1
2
3
4
5
6
7
8
9
10
11
|
static
void
Main(
string
[] args)
{
Task.Run(() =>
//异步开始执行
{
Thread.Sleep(1000);
//异步执行一些任务
Console.WriteLine(
"Hello World"
);
//异步执行完成标记
});
Thread.Sleep(1100);
//主线程在执行一些任务
Console.WriteLine(
"Main Thread"
);
//主线程完成标记
Console.ReadLine();
}
|
发现执行结果是:
这个很正常。可是咱们但愿先执行主线程完成标记,不改动主线程和Task的任务状况下,如何处理?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
static
void
Main(
string
[] args)
{
Say();
//因为Main不能使用async标记
Console.ReadLine();
}
private
async
static
void
Say()
{
var
t = TestAsync();
Thread.Sleep(1100);
//主线程在执行一些任务
Console.WriteLine(
"Main Thread"
);
//主线程完成标记
Console.WriteLine(await t);
//await 主线程等待取异步返回结果
}
static
async Task<
string
> TestAsync()
{
return
await Task.Run(() =>
{
Thread.Sleep(1000);
//异步执行一些任务
return
"Hello World"
;
//异步执行完成标记
});
}
|
1.凡是使用await关键字的方法,都必须打上async标记。
2.async表示方法内有异步方法,调用async方法,会马上另起线程执行。
3.await只是显示等待线程结束。await表示等待异步方法执行完,并取返回值。
既然多线程不能提升运行速度,并且每次请求Asp.net程序都是发起一个新的线程,为何还要用多线程让其“降速”?
为了提升网站的吞吐量。
在MVC中,若是采用异步Action,则会像下面状况执行。
1.请求到达IIS,IIS应用程序池分配一个worker线程用来响应请求。
2.worker线程,执行异步操做,调用CLR线程池线程处理。
3.释放worker线程,响应其余请求。
4.异步操做执行完,w3wp(应用程序池进程)再次分配一个worker线程继续响应。
上述使用场景中,会获取两次worker 线程,这两次获取的线程可能相同,也可能会不一样。若是有比较耗时的任务,很是建议把同步请求转换为异步。
先举个线程不安全的例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
static
void
Main(
string
[] args)
{
Task.Run((Action)Test);
Task.Run((Action)Test);
Console.ReadLine();
}
private
static
void
Test()
{
if
(!IsComplete)
{
//todo other
Thread.Sleep(500);
Console.WriteLine(
"执行完成"
);
IsComplete =
true
;
}
}
public
static
bool
IsComplete {
get
;
set
; }
|
上面的执行结果,这就是线程不安全。(多线程访问同一段代码 产生不肯定结果。)
如何解决,涉及到线程锁的概念。线程锁会让多线程访问的时候,一次只容许一个线程进入。
线程锁例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
private
static
readonly
object
lockObj =
new
object
();
public
static
bool
IsComplete {
get
;
set
; }
static
void
Main(
string
[] args)
{
Task.Run((Action)Test);
Task.Run((Action)Test);
Console.ReadLine();
}
private
static
void
Test()
{
lock
(lockObj)
//锁住的必须是引用类型。因为在静态方法中,则锁住静态引用类型。
{
if
(!IsComplete)
{
//todo other
Thread.Sleep(500);
Console.WriteLine(
"执行完成"
);
IsComplete =
true
;
}
}
}
|
线程锁的技术使一块代码只能一个线程进入。信号量的存在,则是让同一块代码指定多个线程进入。
信号量(SemaphoreSlim)例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
static
readonly
SemaphoreSlim slim =
new
SemaphoreSlim(2);
static
void
Main(
string
[] args)
{
for
(
int
i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(Test, i);
}
Console.ReadLine();
}
private
async
static
void
Test(
object
i)
{
Console.WriteLine(
"准备执行"
+ i);
await slim.WaitAsync();
Console.WriteLine(
"开始执行"
+ i);
//todo other
await Task.Delay(1000);
Console.WriteLine(
"执行结束"
+ i);
slim.Release();
}
|
上面执行结果
从 .NET Framework 4.5 和 Windows 运行时中列出的 API 包含支持异步编程的方法。
应用程序区域 |
包含异步方法的受支持的 API |
---|---|
Web 访问 |
|
使用文件 |
|
使用图像 |
|
WCF 编程 |
转自:http://www.cnblogs.com/neverc/p/4368821.html