并发: 通俗理解就是可以同时做很多事。终端用户程序利用并发功能, 在输入数据库的同时响应用户输入。服务器应用利用并 发,在处理第一个请求的同时响应第二个请求。
多线程: 并发的一种形式, 它采用多个线程来执行程序, 达到并发的效果。
并行处理: 把正在执行的大量的任务分割成小块, 分配给多个同时运行的线程。 对应于现在的多核CPU, 在执行大量任务时, 并行处理把任务分割成小块并分配给多个线程, 让它们在不同的核上独立运行。
Note: 并行处理是多线程的一种, 而多线程是并发的一种
还有一种并发类型: 异步编程
Note: 异步编程的核心理念是异步操作:启动了的操作将会在一段时间后完成。这个操作正在执行时,不会阻塞原来的线程。启动了这个操作的线程,可以继续执行其他任务。当操作完成时, 会通知它的 future, 或者回调函数, 以便让程序知道操作已经结束。
并发编程的另一种形式是响应式编程(reactive programming)。异步编程意味着程序启动一个操作,而该操作将会在一段时间后完成。响应式编程与异步编程非常类似, 不过它是基于异步事件(asynchronous event)的, 而不是异步操作(asynchronous operation)。异步事件可以没有一个实际的开始, 可以在任何时间发生, 并且可以发生多次, 例如用户输入。
响应式编程: 一种声明式的编程, 程序在该模式中对时间做出响应。
现代的异步.NET程序使用两个关键字:async 和 await。 async关键字加在方法声明上, 它的主要目的是使方法内的await关键字生效。如果async方法有返回值, 应返回Task
Note: 不要用void作为async方法的返回类型! async方法可以返回void, 但是这仅限于编写时间处理程序。一个普通的async方法如果没有返回值, 要返回Task, 而不是void。
一个简单的例子:
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace asnyc1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
await DoSomethingAsync(); //此时label1会等DoSomethingAsync完成之后再显示Done, label2先显示26
label1.Text = "Done!";
}
//private void button1_Click(object sender, EventArgs e)
//{
// DoSomethingAsync();
// label1.Text = "Done!"; //此时label1先显示Done, 然后label2再显示26
//}
async Task DoSomethingAsync()
{
int val = 13;
await Task.Delay(TimeSpan.FromSeconds(1)); //wait 1 second asynchronously
val *= 2;
await Task.Delay(TimeSpan.FromSeconds(1)); //wait 1 second asynchronously
label2.Text = val.ToString();
}
}
}
Note: async方法在开始以同步的方式执行。在async方法内部, await 关键字对它的参数执行一个异步等待。它首先检查操作是否完成, 如果完成了, 就继续执行(同步方式)。否则, 它会暂停async方法, 并返回, 留下一个未完成的task。一段时间后, 操作完成, async方法就恢复运行
一个async方法是由多个同步执行的程序块组成的, 每个同步程序块之间由await语句分隔
在上面的代码中,每个同步程序块会试图在原始的上下文中恢复运行。如果在UI线程中调用DoSomethingAsync,这个方法的每个同步程序块都将在此UI 线程上运行。但是,如果在线程池线程中调用,每个同步程序块将在线程池线程上运行。要避免这种错误行为, 可以在await 中使用ConfigureAwait 方法, 将参数continueOnCapturedContext 设为false。接下来的代码刚开始会在调用的线程里运行,在被await 暂停后,则会在线程池线程里继续运行:
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace asnyc1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//private async void button1_Click(object sender, EventArgs e)
//{
// await DoSomethingAsync(); //此时label1会等DoSomethingAsync完成之后再显示Done, label2先显示26
// label1.Text = "Done!";
//}
private void button1_Click(object sender, EventArgs e)
{
DoSomethingAsync();
label1.Text = "Done!"; //此时label1先显示Done, 然后label2再显示26
}
async Task DoSomethingAsync()
{
int val = 13;
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); //wait 1 second asynchronously
val *= 2; //从这里开始在新线程中运行
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); //wait 1 second asynchronously
label2.Text = val.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false; //支持跨线程检查
}
}
}
参考书籍: C#并发编程经典实例
Note: 最好的做法是, 在核心库代码中一直使用ConfigureAwaiter。在外围的用户界面代码中, 只在需要时才恢复上下文。