异步方法
注意,异步方法是C# 5.0后增加
异步(async)方法:用于异步执行其他工作,然后立即返回到调用方法。
await 表达式:用于异步方法内部,指明需要异步执行的任务。一个异步方法可以包含任意多个await表达式,如果一个都不包含的话编译器会发出警告。
class MyDownloadString
{
Stopwatch sw = new Stopwatch();
public void DoRun()
{
const int LargeNumber = 600;
sw.Start();
Task<int> t1 = CountCharactersAsync(1, "http://www.microsoft.com");
Task<int> t2 = CountCharactersAsync(2, "http://illustratedcsharp.com");
CountToALargeNumber(1, LargeNumber);
CountToALargeNumber(2, LargeNumber);
CountToALargeNumber(3, LargeNumber);
CountToALargeNumber(4, LargeNumber);
Console.WriteLine("Chars in http://www.microsoft.com :{0}", t1.Result);
Console.WriteLine("Chars in http://www.illustratedcsharp.com :{0}", t2.Result);
}
//返回值 Task<int>
private async Task<int> CountCharactersAsync(int id, string uriString)
{
WebClient wc1 = new WebClient();
Console.WriteLine("Starting call {0} : {1,4:N0} ms", id, sw.Elapsed.TotalMilliseconds);
string result = await wc1.DownloadStringTaskAsync(new Uri(uriString));
Console.WriteLine(" call {0} completed: {1,4:N0} ms", id, sw.Elapsed.TotalMilliseconds);
return result.Length;
}
//返回值 Task<int>
public static async Task<int> TaskIntAsync()
{
int a = 5, b = 6;
int sum = await Task.Run(() => Sum(a, b));
return sum;
}
//返回值 Task
public static async Task TaskAsync()
{
string a = "task";
await Task.Run(() => Print(ref a));
}
//返回值 Void
public static async void VoidAsync()
{
string a = "void";
await Task.Run(() => Print(ref a));
//此处return无效
}public static int Sum(int a, int b)
{
return a + b;
}
public static void Print(ref string a)
{
Console.WriteLine(a);
}
private void CountToALargeNumber(int id, int value)
{
for (long i = 0; i < value; i++)
{
Console.WriteLine(" End counting {0} : {1,4:N0} ms", id, sw.Elapsed.TotalMilliseconds);
}
}
}
class Program
{
static void Main(string[] args)
{
MyDownloadString ds = new MyDownloadString();
//ds.DoRun();
Task<int> result = MyDownloadString.TaskIntAsync();
Console.WriteLine(result.Result);
MyDownloadString.VoidAsync();
Task task = MyDownloadString.TaskAsync();
task.Wait();
Thread.Sleep(1000);
Console.WriteLine("执行主线程了啊啊啊a");
Console.ReadKey();
}
}
======================================================================
取消异步操作
System.Threading.Task命名空间中有两个类是为此目的而设计的。CancellationToken和CancellationTokenSource
1.CancellationToken 对象包含一个任务十分应被取消的信息。
2.拥有CancellationToken 对象的任务需求要定期检查其令牌(token)状态。如果CancellationToken 对象的IsCancellationRequested属性为true,任务需停止其操作并返回。
3.CancellationToken是不可逆的,并且只能使用一次,一旦IsCancellationRequested属性被设置为true,就不能更改。
4.CancellationTokenSource对象创建可分批给不同任务的CancellationToken对象。任何有CancellationTokenSource的对象都可以条用其Cancel方法,这会将CancellationToken的IsCancellationRequested属性设置为true。
public class Program
{
public static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
MyClass mc = new MyClass();
Task t = mc.RunAsync(token);
Thread.Sleep(3000);//等待3秒
cts.Cancel();//取消操作
t.Wait();
Console.WriteLine("Was Cancelled:{0}",token.IsCancellationRequested);
Console.ReadKey();
}
}
class MyClass
{
public async Task RunAsync(CancellationToken ct)
{
if (ct.IsCancellationRequested)
{
return;
}
await Task.Run(() => CycleMethod(ct),ct);
}
void CycleMethod(CancellationToken ct)
{
Console.WriteLine("Starting CycleMethod");
const int max = 5;
for (int i = 0; i < max; i++)
{
if (ct.IsCancellationRequested)
{
return;
}
Thread.Sleep(1000);
Console.WriteLine(" {0} of {1} iterations completed", i + 1, max);
}
}
}
==========================================================================
在异常调用中同步等待
调用方法可以调用任意多个异步方法并接收他们返回的Task对象,然后你的代码会继续执行其他任务,在某个点需要等待某个特殊Task对象完成,然后继续。为此Task提供了一个Wait,可以在Task对象上调用。
public class Program
{
public static void Main(string[] args)
{
Task t = BadAsync();
t.Wait();
Console.WriteLine("Task Status : {0}", t.Status);
Console.WriteLine("Task IsFaulted:{0}", t.IsFaulted);
Console.ReadKey();
}
static async Task BadAsync()
{
try
{
await Task.Run(() => { throw new Exception(); });
}
catch
{
Console.WriteLine("Exception in BadAsync");
}
}
}
==========================================================================
在异常调用中异步等待
使用Task.WhenAll和Task.WhenAny方法来实现等待一个或所有任务完成。
static class MyDownloadString
{
public static void DoRun()
{
Task<int> t = CountCharactersAsync("http://www.illustratedcsharp.com", "http://www.illustratedcsharp.com");
//t.Wait();
Console.WriteLine("The task has finished returning value{0}.", t.Result);
Console.ReadKey();
}
public static async Task<int> CountCharactersAsync(string site1, string site2)
{
WebClient wc1 = new WebClient();
WebClient wc2 = new WebClient();
Task<string> t1 = wc1.DownloadStringTaskAsync(new Uri(site1));
Task<string> t2 = wc2.DownloadStringTaskAsync(new Uri(site2));
List<Task<string>> tasks = new List<Task<string>>();
tasks.Add(t1);
tasks.Add(t2);
await Task.WhenAll(tasks);
Console.WriteLine("CCA: T1{0} finished", t1.IsCompleted);
Console.WriteLine("CCA: T2{0} finished", t2.IsCompleted);
return t1.IsCompleted ? t1.Result.Length : t2.Result.Length;
}
}
class Program
{
static void Main(string[] args)
{
MyDownloadString.DoRun();
}
}
Task.Delay方法
Task.Delay对象将暂停其在线程中的处理,并在一定时间之后完成。于Thread.Sleep阻塞不同的是,Task.Delay不会阻塞线程,线程可以继续处理其他工作。
//Task.Delay方法
//创建一个Task对象,该对象将暂停其在线程中的处理,并在一定时间后完成,然后与Thread,Sleep阻塞线程不同的是,Task.Delay不会阻塞线程,线程可以继续处理其他工作。
class Simple
{
Stopwatch sw = new Stopwatch();
public void DoRun()
{
Console.WriteLine("Caller:Before call");
ShowDelayAsync();
Console.WriteLine("Caller:After call");
}
private async void ShowDelayAsync()
{
sw.Start();
Console.WriteLine(" Before Delay:{0}", sw.ElapsedMilliseconds);
await Task.Delay(1000);
Console.WriteLine(" After Delay:{0}", sw.ElapsedMilliseconds);
}
}
class Program
{
static void Main(string[] args)
{
Simple ds = new Simple();
ds.DoRun();
Console.Read();
}
}
}
============================================================================
完整的例子
public partial class Form1 : Form{
CancellationTokenSource _cancellationToKenSource;
CancellationToken _cancellationToken;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
button2.Enabled = true;
_cancellationToKenSource = new CancellationTokenSource();
_cancellationToken = _cancellationToKenSource.Token;
int completedPercent = 0;
for (int i = 0; i < 10; i++)
{
if (_cancellationToken.IsCancellationRequested)
{
break;
}
try
{
await Task.Delay(500, _cancellationToken);
completedPercent = (i+1) * 10;
}
catch (TaskCanceledException ex)
{
completedPercent = i * 10;
}
progressBar1.Value = completedPercent;
}
string message = _cancellationToken.IsCancellationRequested?string.Format("Process was cancelled at {0}%.",completedPercent):"Process completed normally.";
MessageBox.Show(message,"Completion Satus");
progressBar1.Value = 0;
}
private void button2_Click(object sender, EventArgs e)
{
if(!button1.Enabled){
button2.Enabled = false;
_cancellationToKenSource.Cancel();
}
}
}