# C#之异步
在异步程序中,程序代码不需要按编写时的顺序严格执行。有时需要在一个新的线程中运行一部分代码,有时无需创建新的线程,但为了更好的利用单个线程的能力,需要改变代码的执行顺序,从而解决在性能或用户体验上导致的难以接受的行为。
在C#5.0引入的一个来用来构建异步方法的新特性---async/await
,但还有其他形式的异步编程的特性,这些特性是.NET框架的一部分,但没有嵌入C#语言,比如BackgroundWorker
类和.NET任务并行库,两者均通过新建线程来实现异步。
async/await
特性的结构
异步的方法在处理完之前就返回到调用方法。C#的async/await
特性可以创建并使用异步方法。该特性有三部分组成。
- 调用方法:该方法调用异步方法,然后在异步方法(可能在相同的线程,也可能在不同的线程)执行其任务的时候继续执行。
- 异步方法,该方法异步执行其工作,然后立即返回到调用方法。
- await表达式:用于异步方法内部,指明需要异步执行的任务,一个异步方法可以包含任意多个await表达式,不过如果一个都不包含的话编译器会发出警告。
什么是异步方法
在语法上,异步方法具有如下特点:
- 方法头中包含
async
方法修饰,且必须出现在返回类型之前,async
关键字是一个上下文关键字,它只是标识该方法包含了一个或多个await表达式,也就是说async
本身并不能创建任何异步操作。 - 方法中包含一个或多个await表达式,用以表示可以异步完成的任务
- 返回类型只有三种:
Task<T>
(该类型,异步方法必须有相应的return T obj与之呼应);Task
(该方法不能有return 语句),且在调用方法控制流中可以设置task.Wait()方法来等待异步方法结束,如果没有的话,调用方法的控制流(主程序)结束 后,异步方法将被迫结束。 - 异步方法的参数可以为任意数量的任意类型,但不能为out或ref参数。
- 按照约定,异步方法的名称应以
Async
为后缀。 - 除了方法外,Lambda表达式和匿名方法也可以作为异步的对象.
(1)返回类型是Task<T>
的异步方法
任何返回Task
namespace ConsoleApp
{
static class DoStuff
{
public static async Task<int> FindSeriesSum() //返回类型是Task<int>与异步方法的return 1相呼应
{
Console.WriteLine("Entering into asyn function");
await Task.Run(WriteN);
Console.WriteLine("Negative over...");
await Task.Run(WriteP);
Console.WriteLine("Positve over...`");
return 1;
}
public static void WriteN()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("-");
}
}
public static void WriteP()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("+");
}
}
}
class Program
{
public static void Main(string[] args)
{
Task<int> task=DoStuff.FindSeriesSum();
Console.WriteLine("wait asyn function...");
var t = task.Result;
Console.WriteLine("all over...");
Console.WriteLine(t);
}
}
}
可以发现,当遇到第一个await
,程序直接返回调用方法的控制流(打印出wait async function
,此后便在task.Wait
处等待异步方法的结束),同时开始执行await后的任务,执行完第一个await任务,在异步控制流中继续前进,打印完Negative over...
后,又碰到第二个await任务,返回到调用方法的控制流,此时,实际上调用方法的控制流处于等待状态,在task.Result
处,同时开始执行第二个任务,直到执行完毕后退出(打印Positive over...
),调用方法的控制流继续前进,打印all over...
,整个程序结束。
当去掉task.Result
后,发现调用程序结束,异步方法也被迫结束。
public static void Main(string[] args)
{
Task<int> task=DoStuff.FindSeriesSum();
Console.WriteLine("wait asyn function...");
Console.WriteLine("all over...");
}
(2)返回类型是Task
的异步方法
namespace ConsoleApp
{
static class DoStuff
{
public static async Task FindSeriesSum()//返回值是Task,这意味着在调用方法的控制流中必须使用task.Wait()方法来使调用方法的控制流在此处等异步方法执行完毕,再继续前进。
{
Console.WriteLine("Entering into asyn function");
await Task.Run(WriteN);
Console.WriteLine("Negative over...");
await Task.Run(WriteP);
Console.WriteLine("Positve over...`");
return;//可有可无,若有的话,像这样即使异步方法出现了return,也不会返回任何东西,它只是退出了。
}
public static void WriteN()
{
for(int i = 0; i < 2000; i++)
{
Console.Write("-");
}
}
public static void WriteP()
{
for(int i = 0; i < 2000; i++)
{
Console.Write("+");
}
}
}
class Program
{
public static void Main(string[] args)
{
var sometask=DoStuff.FindSeriesSum();
Console.WriteLine("wait asyn function...");
sometask.Wait();
Console.WriteLine("all over...");
}
private static void CountBig(int p)
{
for (int i = 0; i < p; i++) ;
}
}
}
可以发现,当遇到第一个await
,程序直接返回调用方法的控制流(打印出wait async function
,此后便在task.Wait
处等待异步方法的结束),同时开始执行await后的任务,执行完第一个await任务,在异步控制流中继续前进,打印完Negative over...
后,又碰到第二个await任务,返回到调用方法的控制流,此时,实际上调用方法的控制流处于等待状态,在someTask.Wait()
处,同时开始执行第二个任务,直到执行完毕后退出(打印Positive over...
),调用方法的控制流继续前进,打印all over...
,整个程序结束。
同样地,当去掉task.Wait
,会发现随着调用方法的控制流结束,异步方法的控制流也被迫结束。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace threadDemo
{
static class DoStuff
{
public static async Task FindSeriesSum()//返回值是Task,这意味着在调用方法的控制流中必须使用task.Wait()方法来使调用方法的控制流在此处等异步方法执行完毕,再继续前进。
{
Console.WriteLine("Entering into asyn function");
await Task.Run(WriteN);
Console.WriteLine("Negative over...");
await Task.Run(WriteP);
Console.WriteLine("Positve over...`");
return;//可有可无,若有的话,像这样即使异步方法出现了return,也不会返回任何东西,它只是退出了。
}
public static void WriteN()
{
for (int i = 0; i < 2000; i++)
{
Console.Write("-");
}
}
public static void WriteP()
{
for (int i = 0; i < 2000; i++)
{
Console.Write("+");
}
}
}
class Program
{
public static void Main(string[] args)
{
var sometask = DoStuff.FindSeriesSum();
Console.WriteLine("wait asyn function...");
for (int i = 0; i < 200; i++)
Console.Write(i);
sometask.Wait();
Console.WriteLine("all over...");
}
private static void CountBig(int p)
{
for (int i = 0; i < p; i++) ;
}
}
}
output:
Entering into asyn function
wait asyn function...
01234567891011121314151617181920--------2122----2324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Negative over...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Positve over...`
all over...
(3)返回类型是void的异步方法,即fire and forget
模式
namespace ConsoleApp
{
static class DoStuff
{
public static async void FindSeriesSum() //返回类型是void,所以是fire and forget模式,这意味着,调用方法的控制流结束后,异步方法的控制流也必须停止。
{
Console.WriteLine("Entering into asyn function");
await Task.Run(WriteN);
Console.WriteLine("Negative over...");
await Task.Run(WriteP);
Console.WriteLine("Positve over...`");
return;//可有可无,若有,像这样,它并不返回任何东西,只是退出了。
}
public static void WriteN()
{
for(int i = 0; i < 2000; i++)
{
Console.Write("-");
}
}
public static void WriteP()
{
for(int i = 0; i < 2000; i++)
{
Console.Write("+");
}
}
}
class Program
{
public static void Main(string[] args)
{
DoStuff.FindSeriesSum();
Console.WriteLine("wait asyn function...");
Console.WriteLine("all over...");
}
}
}
可以看到,异步方法中的控制流在调用方法的控制流结束时,被打断。
class Program
{
public static async Task DoRun()
{
Console.WriteLine("Enter DoRun...");
await Task.Run(WriteP);
Console.WriteLine("Now WriteN...");
await Task.Run(WriteN);
}
public static void WriteP()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("+");
}
}
public static void WriteN()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("-");
}
}
static void Main()
{
Console.WriteLine("Test begin...");
Task task = DoRun();
Console.WriteLine("Sleep..");
Thread.Sleep(5);
Console.WriteLine("After sleep...");
task.Wait();
Console.WriteLine("Over...");
}
}
output:
Test begin...
Enter DoRun...
+++++Sleep..
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Now WriteN...
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------After sleep...
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Over...
await
表达式
await
表达式指定了一个异步执行的任务。语法非常简单,就是await关键字,后面跟上一个awaitable
类型的实例,万幸的是,我们并不需要构建自己的awaitable
,我们可以使用Task
类,Task
类就是awaitable
类型,这也能满足我们大多数需求。在.NET4.5后,还可以用Task<T>
类型对象,它也是awaitable
类型,用以和await
关键字搭配,组成用以异步执行的任务。
而创建一个Task
类,最简单的就是使用Task.Run
方法来创建一个Task
,其中Task.Run
的重载的返回类型和签名如下表:
返回类型 | 签名 |
---|---|
Task | Run(Action action) |
Task | Run(Action action,CancellationToken token) |
Task |
Run(Func |
Task |
Run(Func |
Task | Run(Func |
Task | Run(Func |
Task |
Run(Func<Task |
Task |
Run(Func<Task |
public static void Main(string[] args)
{
Console.WriteLine("Test begin...");
Task task = DoWorkAsyn();
Console.WriteLine("wait async function");
task.Wait();
Console.WriteLine("Press any key to exit");
Console.Read();
}
public static async Task DoWorkAsyn()
{
await Task.Run(()=>Console.WriteLine(5.ToString()));
Console.WriteLine((await Task.Run(()=>6)).ToString());
await Task.Run(() => Task.Run(() => Console.WriteLine(7.ToString())));
int value = await Task.Run(() => Task.Run(() => 8));
Console.WriteLine(value);
}
output:
Test begin...
wait async function
5
6
7
8
Press any key to exit
- 取消一个异步操作
System.Threading.Tasks
命名控件有两个类是为此目的而设计的:CancellationToken
,CancellationTokenSource
。
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);
//cts.Cancel();
t.Wait();
Console.WriteLine("Was Cancelled:{0}", token.IsCancellationRequested);
}
}
class MyClass
{
public async Task RunAsync(CancellationToken ct)
{
Console.WriteLine("Enter in RunAsync");
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);
}
}
}
output:
Enter in RunAsync
Starting CycleMethod
1 of 5 iterations completed
2 of 5 iterations completed
3 of 5 iterations completed
4 of 5 iterations completed
5 of 5 iterations completed
Was Cancelled:False
取消注释后:
output:
Enter in RunAsync
Starting CycleMethod
1 of 5 iterations completed
2 of 5 iterations completed
3 of 5 iterations completed
Was Cancelled:True
- 在调用方法中同步的等待任务
class Program
{
public static async Task DoRun(Action act)
{
Console.WriteLine("Enter DoRun...");
await Task.Run(act);
}
public static void WriteP()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("+");
}
}
public static void WriteN()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("-");
}
}
static void Main()
{
Task task1 = DoRun(WriteP);
Task task2 = DoRun(WriteN);
Task.WaitAll(task1, task2);
Console.WriteLine("Task 1:{0} Finished", task1.IsCompleted ? "" : "Not");
Console.WriteLine("Task 2:{0} Finished", task2.IsCompleted ? "" : "Not");
}
}
output:
Enter DoRun...
++++++++++++++Enter DoRun...
+++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Task 1: Finished
Task 2: Finished
将上面的Task.WaitAll
改为Task.WaitAny
,output:
Enter DoRun...
Enter DoRun...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------Task 1: Finished
Task 2:Not Finished
------------------------------------------------
- 在异步方法中异步地等待任务
class Program
{
public static async Task DoRun()
{
Console.WriteLine("Enter DoRun...");
Task task1 = Task.Run(WriteP);
Task task2 = Task.Run(WriteN);
await Task.WhenAll(new List<Task> { task1, task2 });
Console.WriteLine("Task 1:{0} Finished", task1.IsCompleted ? "" : "Not");
Console.WriteLine("Task 2:{0} Finished", task2.IsCompleted ? "" : "Not");
}
public static void WriteP()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("+");
}
}
public static void WriteN()
{
for(int i = 0; i < 1000; i++)
{
Console.Write("-");
}
}
static void Main()
{
Console.WriteLine("Test begin...");
Task tasks = DoRun();
tasks.Wait();
Console.WriteLine("Over...");
}
}
output:
Test begin...
Enter DoRun...
++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Task 1: Finished
Task 2: Finished
Over...
在异步方法中,异步的等待,直到两个任务都完成了,才在异步方法中继续进行下一步,因此打印完毕+
和-
后,最后在异步中给出两个任务的状态。
static void Main()
{
Task t = BadAsyn();
t.Wait();
Console.WriteLine("Task status:{0}", t.Status);
Console.WriteLine("Task IsFaulted:{0}", t.IsFaulted);
}
public static async Task BadAsyn()
{
try
{
await Task.Run(() => throw new Exception());
}
catch
{
Console.WriteLine("Exception in BadAsync");
}
}
output:
Exception in BadAsync
Task status:RanToCompletion
Task IsFaulted:False
Task.Delay()
static void Main()
{
Console.WriteLine("Test begins...");
TestAsyn();
Console.WriteLine("Test ends...");
Console.ReadLine();//阻塞调用方法所在的主线程控制流
}
public static async void TestAsyn()
{
Console.WriteLine("Enter testAsyn");
await Task.Delay(500);//异步方法控制流中的“暂停”,不影响调用方法主线程控制流
Console.WriteLine("After Delay...");
}
output:
Test begins...
Enter testAsyn
Test ends...
After Delay...
在GUI中执行异步操作
实际上,异步方法在GUI程序上非常有用,因为GUI程序在设计上要求所有的显示变化都必须在主GUI线程中完成,如点击按钮,展示标签,移动窗体等(各种消息),Windows是通过消息来实现这一点的,消息被放入由消息泵管理的消息队列中。
消息泵从队列中取出一条消息,并调用它的处理程序代码,当处理程序代码完成后,消息泵获取下一个消息,并循环此过程。 也正因为此,处理程序就必须执行时间要很短,这样才不至于挂起阻碍其他消息的处理,否则消息队列中的消息产生积压,程序失去相应响应。
<StackPanel>
<Label Name="lblStatus" Margin="10,5,10,0">Not Doing Anything</Label>
<Button x:Name="btnDoStuff" Content="Do Stuff" HorizontalAlignment="Left" Margin="10,5" Padding="5,2" Click="btnDoStuff_Click"/>
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnDoStuff_Click(object sender, RoutedEventArgs e)
{
btnDoStuff.IsEnabled = false;
lblStatus.Content = "Doing Stuff";
//Thread.Sleep(4000);
await Task.Delay(4000);
lblStatus.Content = "Not Doing Anything";
btnDoStuff.IsEnabled = true;
}
}
使用async/await
可以将该点击消息的前两条消息压入队列,然后将自己从处理器“摘下”,4秒后将自己剩余部分压入队列。
BackgroundWorker
类
async/awaiter
特性更适合那些需要在后台完成的不想关的小任务,但有时候,可能需要另建一个线程,在后台持续运行以完成某项工作,并不时地与主线程进行通信,而这正是BackgroundWorker
所要做的。
BackgroundWorker
的属性有WokerReportsProgress
,WokerSupportsCancellation
,IsBusy
,CancellationPending
,方法有RunWorkerAsync()
,CancelAsync()
,ReportsProgress()
,事件有DoWork
,ProgressChanged
,RunWorkerCompleted
。
其中:
WorkerReportsProgress
用于设置BackgroundWorker
是否将它的进度汇报给主线程,WorkerSupportsCancellation
用于设置是否允许主线程取消BackgroundWorker
的工作。
RunWorkerAsync
方法负责在主线程中启动BackgroundWorker
,同时触发DoWork
事件,而在DoWork
事件中,可以添加需要完成的任务的代码,如果要给主线程汇报进度,可调用ReportsProgress()
方法,而ReportsProgress()
方法又触发ProgressChanged
事件,后台程序正在工作,则其IsBusy
属性就是true。
如果允许主线程取消后台线程任务,主线程调用CancelAsync
方法,该方法会使BackgroundWorker
的IsCancellationPending
属性为true。
事件处理程序的委托如下:
void DoWorkEventHandler(object sender,DoWorkEventArgs e);
void ProgressChangedEventHandler(object sender,ProgressChangedEventArgs e);
void RunWorkerCompletedEventHandler(object sender,RunWorkerCompletedEventArgs e);
这些事件处理程序的sender自然都是发起事件的BackgroundWorker
,关于args的说明参考微软文档:
DoworkEventArgs
ProgressChangedEventArgs
ProgressPercentage
是传给主线程的后台线程任务完成的进度值,也是ReportsProcess
的参数。

RunWorkerCompletedEventArgs
例子:
<StackPanel>
<ProgressBar x:Name="progressBar" Height="20" Width="200" Margin="10"/>
<Button x:Name="btnProcess" Width="100" Click="btnDoStuff_Click">Process</Button>
<Button x:Name="btnCancel" Width="100" Click="btnCancel_Click">Cancel</Button>
</StackPanel>
public partial class MainWindow : Window
{
BackgroundWorker bgworker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
bgworker.WorkerReportsProgress = true;//允许向主线程汇报进度
bgworker.WorkerSupportsCancellation = true;//允许主线程取消后台任务
bgworker.DoWork += DoWork_Handler;//注册DoWork事件
bgworker.ProgressChanged += ProgressChanged_Handler;//注册ProgressChanged事件
bgworker.RunWorkerCompleted += Bgworker_RunWorkerCompleted;//注册RunWorkCompleted事件
}
private void Bgworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//完成后的操作,不管是正常结束还是被取消的
progressBar.Value = 0;
if (e.Cancelled)
MessageBox.Show("被取消", "有内鬼,任务取消");
else
MessageBox.Show("完成", "任务完成");
}
private void btnDoStuff_Click(object sender, RoutedEventArgs e)
{
if (!bgworker.IsBusy)
{
bgworker.RunWorkerAsync(); //触发Dowork事件
}
}
private void DoWork_Handler(object sender,DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for(int i = 1; i <= 10; i++)
{
if (worker.CancellationPending) //不断检查Cancellation属性,如果true,则将e的Cancel属性
{ //设置为true,这样RunCompletedEventArgs的Cancelled属性就也被设定为true
e.Cancel = true;
break;
}
else
{
worker.ReportProgress(i * 10);//否则,向主线程汇报进度,触发ProgressChanged事件,参数传给该事件的
//ProgressChangedEventArgs的ProgressPercentage属性
Thread.Sleep(500);//模拟time-consuming 任务
}
}
}
private void ProgressChanged_Handler(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;//将e的ProgressPerceentage值传入进度条
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
bgworker.CancelAsync();
}
}


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix