具有现成的,具有ddns功能的dns服务器程序 使用了dynv6,发现挺好用的,想着,能不能自己在局域网搭建一个具有类似的,具有ddns功能的dns服务器
有小伙伴问我,为什么不推荐他使用 Task.Factory.StartNew ,因为 Task.Run 是比较新的方法。本文告诉大家 Task.Run 和 Task.Factory.StartNew 区别有很多博客说到了 Task.Run 和 Task.Factory.StartNew 区别,所以我也就不需要展开告诉大家。只需要知道 Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制。可以认为 Task.Run 是简化的 Task.Factory.StartNew 的使用,除了需要指定一个线程是长时间占用的,否则就使用 Task.Run创建新线程下面来告诉大家使用两个函数创建新的线程 Task.Run(() => { var foo = 2; }); 这时 foo 的创建就在另一个线程,需要知道 Task.Run 用的是线程池,也就是不是调用这个函数就会一定创建一个新的线程,但是会在另一个线程运行。 Task.Factory.StartNew(() => { var foo = 2; }); 可以看到,两个方法实际上是没有差别,但是Task.Run比较好看,所以推荐使用Task.Run。等待线程创建的线程,如果需要等待线程执行完成在继续,那么可以使用 await 等待 private static async void SeenereKousa() { Console.WriteLine("开始 线程"+Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Console.WriteLine("进入 线程" + Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("退出 线程"+Thread.CurrentThread.ManagedThreadId); } 但是需要说的是这里使用 await 主要是给函数调用的外面使用,上面代码在函数里面使用 await 函数是 void 那么和把代码放在 task 里面是相同 private static async void SeenereKousa() { Console.WriteLine("开始 线程"+Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Console.WriteLine("进入 线程" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("退出 线程"+Thread.CurrentThread.ManagedThreadId); }); } 但是如果把 void 修改为 Task ,那么等待线程才有用除了使用 await 等待,还可以使用 WaitAll 等待 Console.WriteLine("开始 线程" + Thread.CurrentThread.ManagedThreadId); var t = Task.Run(() => { Console.WriteLine("进入 线程" + Thread.CurrentThread.ManagedThreadId); }); Task.WaitAll(t); Console.WriteLine("退出 线程" + Thread.CurrentThread.ManagedThreadId); 使用 WaitAll 是在调用 WaitAll 的线程等待,也就是先在线程 1 运行,然后异步到 线程2 运行,这时线程1 等待线程2运行完成再继续,所以输出开始 线程1 进入 线程2 退出 线程1 长时间运行两个函数最大的不同在于 Task.Factory.StartNew 可以设置线程是长时间运行,这时线程池就不会等待这个线程回收 Task.Factory.StartNew(() => { for (int i = 0; i < 100; i++) { var foo = 2; } Console.WriteLine("进行 线程" + Thread.CurrentThread.ManagedThreadId); }, TaskCreationOptions.LongRunning); 所以在需要设置线程是长时间运行的才需要使用 Task.Factory.StartNew 不然就使用 Task.Run调用 Task.Run(foo) 就和使用下面代码一样Task.Factory.StartNew(foo, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); 实际上 Task.Run(foo) 可以认为是对 Task.Factory.StartNew 封装,使用简单的默认的参数。如果需要自己定义很多参数,就请使用 Task.Factory.StartNew 定义参数。
1 .NET多线程是什么?1.1 进程与线程进程是一种正在执行的程序。线程是程序中的一个执行流。多线程是指一个程序中可以同时运行多个不同的线程来执行不同的任务。1.2 .NET中的线程Thread是创建和控制线程的类。ManagedThreadId是线程ID。CurrentThread是获取当前正在运行的线程。1.3 同步与异步同步是调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。(单线程)异步调用一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。(多线程)1.4 .NET中的多线程发展主要有Thread,ThreadPool,TaskThread就是线程,需要自己调度,直接跟系统对接,相对管理比较复杂及效率差。ThreadPool是Thread的一个升级版,ThreadPool是从线程池中获取线程,如果线程池中又空闲的元素,则直接调用,如果没有才会创建,而Thread则是会一直创建新的线程,要知道开启一个线程就算什么事都不做也会消耗大约1m的内存,是非常浪费性能的。但是ThreadPool提供的接口比较少。Task和ThreadPool是一样的,都是从线程池中取空闲的线程。比ThreadPool调用接口更加丰富。目前.Net使用多线程管理,应该优先使用Task。代码: /// <summary> /// 多线程发展历史 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnHistory_Click(object sender, EventArgs e) { Console.WriteLine($"Thread start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); var threadStart = new ThreadStart(DoNothing); var thread = new Thread(threadStart); thread.Start(); Console.WriteLine($"Thread end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(3000); Console.WriteLine($"ThreadPool start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); var callback = new WaitCallback(DoNothing); ThreadPool.QueueUserWorkItem(callback); Console.WriteLine($"ThreadPool end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(3000); Console.WriteLine($"Task start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Action action = DoNothing; Task task = new Task(action); task.Start(); Console.WriteLine($"Task end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } 2 为什么需要多线程?特点:卡界面:单线程卡,多线程不卡性能好:单线程差,多线程好(资源换性能)执行顺序:单线程顺序,多线程无序代码: /// <summary> /// 同步(单线程) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSync_Click(object sender, EventArgs e) { Console.WriteLine($"btnSync_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); for (int i = 0; i < 5; i++) { DoNothing(); } Console.WriteLine($"btnSync_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } /// <summary> /// 异步(多线程) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAsync_Click(object sender, EventArgs e) { Console.WriteLine($"btnAsync_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); for (int i = 0; i < 5; i++) { var ts = new ThreadStart(DoNothing); var t = new Thread(ts); t.Start(); } Console.WriteLine($"btnAsync_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } private void DoNothing() { Console.WriteLine($"DoNothing start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(2000); Console.WriteLine($"DoNothing end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } 3 如何使用.NET多线程?3.1 Task3.1.1 创建任务1、通过调用任务类构造函数实例化,但通过调用其Start()启动任务。Task t1 = new Task(action, "alpha"); t1.Start(); 2、通过调用TaskFactory.StartNew(Action < Object>,Object)方法在单个方法调用中实例化和启动任务。 Task t2 = Task.Factory.StartNew(action, "beta"); 3、通过调用Run(Action)方法在单个方法调用中实例化和启动任务。Task t3 = Task.Run(action); 3.1.2 从任务中返回值Result 属性将阻止调用线程,直到任务完成。Task<int> task1 = Task<int>.Factory.StartNew(() => 1); int i = task1.Result; 3.1.3 等待任务完成可以通过调用 Wait 方法来等待一个或多个任务完成,从而同步调用线程的执行以及它启动的异步任务。调用无参数 Wait() 方法以无条件等待,直到任务完成。调用Wait(Int32)和 Wait(TimeSpan) 方法会阻止调用线程,直到任务完成或超时间隔(以先达到者为准)为止。调用WaitAny(Task[])方法等待一组任务中第一个任务完成。调用WaitAll(Task[])方法来等待一系列任务全部完成。3.1.4 异常处理调用代码可以通过使用 try/catch 块中的以下任意方法来处理异常:await tasktask.Wait()task.Resulttask.GetAwaiter().GetResult()代码:var task1 = Task.Run(() => { throw new Exception("This exception is expected!"); }); try { task1.Wait(); } catch (Exception e) { Console.WriteLine(e.Message); } Console.ReadKey(); 3.1.5 取消任务你可以使用 CancellationTokenSource 类在以后某一时间发出取消请求。static void Main(string[] args) { var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var tasks = new ConcurrentBag<Task>(); Task tc; for (int i = 0; i < 10; i++) { var k = i; tc = Task.Run(() => DoNothing(k, token), token); tasks.Add(tc); } char ch = Console.ReadKey().KeyChar; if (ch == 'c' || ch == 'C') { tokenSource.Cancel(); Console.WriteLine("\n开始取消任务."); } try { Task.WhenAll(tasks.ToArray()); } catch (OperationCanceledException) { Console.WriteLine($"\n{nameof(OperationCanceledException)} thrown\n"); } finally { tokenSource.Dispose(); } Console.ReadKey(); } private static void DoNothing(int i, CancellationToken ct) { Console.WriteLine($"DoNothing start index:{i} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(i * 1000); if (ct.IsCancellationRequested) { Console.WriteLine($"任务已取消 index:{i} id:{Thread.CurrentThread.ManagedThreadId} "); ct.ThrowIfCancellationRequested(); } Console.WriteLine($"DoNothing end index:{i} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } 3.1.6 实际案例代码:/// <summary> /// Task实际案例 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnTask_Click(object sender, EventArgs e) { Console.WriteLine($"项目组接到任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Console.WriteLine($"项目经理设计数据库,设计原型,分配任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); List<Task> tasks = new List<Task>(); tasks.Add(Task.Run(() => Coding("赵XX","前端页面"))); tasks.Add(Task.Run(() => Coding("王XX", "IOS页面"))); tasks.Add(Task.Run(() => Coding("黄XX", "后端接口"))); tasks.Add(Task.Run(() => Coding("杜XX", "后端接口"))); TaskFactory taskFactory = new TaskFactory(); taskFactory.ContinueWhenAll(tasks.ToArray(), t => { Console.WriteLine($"项目经理发布,测试人员测试任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); }); } private void Coding(string personName,string taskName) { Console.WriteLine($"{personName}开发{taskName} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(2000); } 3.2 async和awaitAsync 和 Await几乎与创建同步方法一样创建异步方法。代码:/// <summary> /// Async和Await应用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAsyncAndAwait_Click(object sender, EventArgs e) { Console.WriteLine($"btnAsyncAndAwait_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); DoNothingAsync(); Console.WriteLine($"btnAsyncAndAwait_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } private async Task DoNothingAsync() { Console.WriteLine($"DoNothingAsync start id:{Th
await 和 async异步的实质就是线程的切换(遇到await),同一请求下,异步和同步的执行时间是一样的,但有人说异步可以提高XXX性能,但具体是什么性能呢?又说不上来,其实就只提高并发量,并不能提升你应用程序处理的速度使用异步的目的是用尽量少的线程保证相同的并发量。线程的总数少了,线程切换消耗的资源就小了,相对来提供给客户任务的资源就多了,性能就在这里。CPU 在大量线程的情况下,20%的时间片在线程切换上,客户任务有80%的资源使用。减少了线程数,只有5%的时间片消耗在线程切换,客户任务有95%的资源使用。减少不必要的线程等待,原来线程工作50%+等待闲置50%,现在把等待闲置50%省掉,就会提升1倍的性能。在方法定义返回类型的前面增加async关键字表示该方法为异步方法。而返回值必须是void 或者为Task<T>。T为返回值的类型;在调用被async修饰过的异步方法时可以使用await关键字变为同步方法,语法为调用前增加await关键字;string result = await loadString();方法名称一般是Async 结尾。可以包含一个或者多个await表达式。异步方法的参数不能使用ref和out参数。方法头包含async关键字,并且在返回类型之前。除了方法之外, Lambda 表达式和匿名函数也可以作为异步对象。async void该方式声明的方法是无法使用 catch 捕获异常的,所以以下代码的 try、catch 并没什么卵用。在 Task 上加上 ConfigureAwait(false),此设置代表当 async 中的异步任务完成后,不读取当时调用它的原线程的上下文信息,而是在线程池上下文中执行 async 方法的剩余部分。
一些示例取消一个异步任务或一组任务(C#)。在一段时间后取消异步任务 (C#)在完成一个异步任务后取消剩余任务 (C#)启动多个异步任务并在其完成时进行处理 (C#)项目将创建一个 UI,其中包含用于启动进程和取消进程的按钮,如下图所示。 这些按钮名为 startButton 和 cancelButton。取消一个异步任务或一组任务 (C#)如果不想等待异步应用程序完成,可以设置一个按钮用来取消它。 通过遵循本主题中的示例,可以为下载一个或一组网站内容的应用程序添加一个取消按钮。取消一个任务下列代码是取消单个任务示例的完整 MainWindow.xaml.cs 文件。using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net.Http; using System.Threading; namespace CancelATask { public partial class MainWindow : Window { //声明一个System.Threading.CancellationTokenSource. CancellationTokenSource cts; public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { //实例化CancellationTokenSource. cts = new CancellationTokenSource(); resultsTextBox.Clear(); try { //如果请求取消,则发送令牌以携带消息。 int contentLength = await AccessTheWebAsync(cts.Token); resultsTextBox.Text += String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength); } catch (OperationCanceledException) { //如果请求取消,则返回OperationCanceledException resultsTextBox.Text += "\r\nDownload canceled.\r\n"; } catch (Exception) { resultsTextBox.Text += "\r\nDownload failed.\r\n"; } //下载完成后,将CancellationTokenSource设置为空 cts = null; } //为Cancel 按钮添加一个事件处理程序。 private void cancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } } //提供一个CancellationToken参数 async Task<int> AccessTheWebAsync(CancellationToken ct) { HttpClient client = new HttpClient(); resultsTextBox.Text += String.Format("\r\nReady to download.\r\n"); //您可能需要放慢速度才能有机会取消。 await Task.Delay(2500); //GetAsync返回一个Task<HttpResponseMessage> //如果选择Cancel按钮,则ct参数将携带消息。 HttpResponseMessage response = await client.GetAsync("http://www.twitter.com/dd470362.aspx", ct); //从HttpResponseMessage中检索网站内容 byte[] urlContents = await response.Content.ReadAsByteArrayAsync(); //该方法的结果是下载网站页面的字符长度 return urlContents.Length; } } // 成功下载的输出: // Ready to download. // Length of the downloaded string: 158125. // 或者,如果取消: // Ready to download. // Download canceled. } 取消任务列表下列代码是取消任务列表示例的完整 MainWindow.xaml.cs 文件。using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net.Http; using System.Threading; namespace CancelAListOfTasks { public partial class MainWindow : Window { //声明一个System.Threading.CancellationTokenSource. CancellationTokenSource cts; public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { cts = new CancellationTokenSource(); resultsTextBox.Clear(); try { await AccessTheWebAsync(cts.Token); resultsTextBox.Text += "\r\nDownloads complete."; } catch (OperationCanceledException) { resultsTextBox.Text += "\r\nDownloads canceled."; } catch (Exception) { resultsTextBox.Text += "\r\nDownloads failed."; } cts = null; } //为Cancel 按钮添加一个事件处理程序。 private void cancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } } //将返回类型更改为Task,因为该方法没有返回语句。 async Task AccessTheWebAsync(CancellationToken ct) { HttpClient client = new HttpClient(); //调用SetUpURLList来创建一个Web地址列表 List<string> urlList = SetUpURLList(); //添加一个循环来处理网址列表 foreach (var url in urlList) { //GetAsync返回一个Task<HttpResponseMessage> //如果选择Cancel按钮,则ct参数将携带消息。 //请注意,取消按钮可以取消所有剩余的下载 HttpResponseMessage response = await client.GetAsync(url, ct); //从HttpResponseMessage中检索网站内容 byte[] urlContents = await response.Content.ReadAsByteArrayAsync(); resultsTextBox.Text += String.Format("\r\nLength of the downloaded string: {0}.\r\n", urlContents.Length); } } private List<string> SetUpURLList() { List<string> urls = new List<string> { "http://www.twitter.com", "http://www.twitter.com/hh290138.aspx", "http://www.twitter.com/hh290140.aspx", "http://www.twitter.com/dd470362.aspx", "http://www.twitter.com/aa578028.aspx", "http://www.twitter.com/ms404677.aspx", "http://www.twitter.com/ff730837.aspx" }; return urls; } } //如果您不选择取消,则输出: //Length of the downloaded string: 35939. //Length of the downloaded string: 237682. //Length of the downloaded string: 128607. //Length of the downloaded string: 158124. //Length of the downloaded string: 204890. //Length of the downloaded string: 175488. //Length of the downloaded string: 145790. //Downloads complete. //如果您选择取消: //Length of the downloaded string: 35939. //Length of the downloaded string: 237682. //Length of the downloaded string: 128607. //Downloads canceled. } 在一段时间后取消异步任务 (C#)如果不希望等待操作结束,可使用 CancellationTokenSource.CancelAfter 方法在一段时间后取消异步操作。 此方法会计划取消未在 CancelAfter 表达式指定的时间段内完成的任何关联任务。完整的示例请注意,必须为 System.Net.Http 添加引用。using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net.Http; using System.Threading; namespace CancelAfterTime { public partial class MainWindow : Window { CancellationTokenSource cts; public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { cts = new CancellationTokenSource(); resultsTextBox.Clear(); try { //设置CancellationTokenSource以在2.5秒后取消。 (您可以调整时间。) cts.CancelAfter(2500); await AccessTheWebAsync(cts.Token); resultsTextBox.Text += "\r\nDownloads succeeded.\r\n"; } catch (OperationCanceledException) { resultsTextBox.Text += "\r\nDownloads canceled.\r\n"; } catch (Exception) { resultsTextBox.Text += "\r\nDownloads failed.\r\n"; } cts = null; } //您仍然可以使用取消按钮。 private void cancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } } async Task AccessTheWebAsync(CancellationToken ct) { HttpClient client = new HttpClient(); List<string> urlList = SetUpURLList(); foreach (var url in urlList) { //取消按钮取消所有剩余的下载。 HttpResponseMessage response = await client.GetAsync(url, ct); //从HttpResponseMessage中检索网站内容 byte[] urlContents = await response.Content.ReadAsByteArrayAsync(); resultsTextBox.Text += String.Format("\r\nLength of the downloaded string: {0}.\r\n", urlContents.Length); } } private List<string> SetUpURLList() { List<string> urls = new List<string> { "http://www.twitter.com", "http://www.twitter.com/ee256749.aspx", ... }; return urls; } } //示例输出: // Length of the downloaded string: 35990. // Length of the downloaded string: 407399. // Length of the downloaded string: 226091. // Downloads canceled. } 在完成一个异步任务后取消剩余任务 (C#)通过结合使用 Task.WhenAny 方法和 CancellationToken,可在一个任务完成时取消所有剩余任务。WhenAny 方法采用任务集合中的一个参数。该方法启动所有任务,并返回单个任务。 当集合中任意任务完成时,完成单个任务。此示例演示如何结合使用取消标记与 WhenAny 保留任务集合中第一个要完成的任务,并取消剩余任务。 每个任务都下载网站内容。本示例显示第一个完成的下载的内容长度,并取消其他下载。完整的示例using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net.Http; using System.Threading; namespace CancelAfterOneTask { public partial class MainWindow : Window { CancellationTokenSource cts; public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { cts = new CancellationTokenSource(); resultsTextBox.Clear(); try { await AccessTheWebAsync(cts.Token); resultsTextBox.Text += "\r\nDownload complete."; } catch (OperationCanceledException) { resultsTextBox.Text += "\r\nDownload canceled."; } catch (Exception) { resultsTextBox.Text += "\r\nDownload failed."; } cts = null; } private void cancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } } async Task AccessTheWebAsync(CancellationToken ct) { HttpClient client = new HttpClient(); List<string> urlList = SetUpURLList(); //创建一个查询,该查询在执行时返回一组任务。 IEnumerable<Task<int>> downloadTasksQuery = from url in urlList select ProcessURLAsync(url, client, ct); //使用ToArray执行查询并开始下载任务 Task<int>[] downloadTasks = downloadTasksQuery.ToArray(); //调用WhenAny然后等待结果。首先完成的任务分配给firstFinishedTask。 Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks); //取消下载的其余部分,你只是想要第一个。 cts.Cancel(); //等待第一个完成的任务并显示结果。 var length = await firstFinishedTask; resultsTextBox.Text += String.Format("\r\nLength of the downloaded website: {0}\r\n", length); } //将网站的处理步骤集中到一个异步方法中。 async Task<int> ProcessURLAsync(string url, HttpClient client, CancellationToken ct) { //GetAsync返回一个Task<HttpResponseMessage> HttpResponseMessage response = await client.GetAsync(url, ct); //从HttpResponseMessage中检索网站内容 byte[] urlContents = await response.Content.ReadAsByteArrayAsync(); return urlContents.Length; } private List<string> SetUpURLList() { List<string> urls = new List<string> { "http://www.twitter.com", "http://www.twitter.com/hh290138.aspx", "http://www.twitter.com/hh290140.aspx", ... }; return urls; } } //示例输出: // Length of the downloaded website: 158856 // Download complete. } 启动多个异步任务并在其完成时进行处理 (C#)通过使用 Task.WhenAny,可以同时启动多个任务,并在它们完成时逐个对它们进行处理,而不是按照它们的启动顺序进行处理。下面的示例使用查询来创建一组任务。 每个任务都下载指定网站的内容。 在对 while 循环的每次迭代中,对 WhenAny 的等待调用返回任务集合中首先完成下载的任务。 此任务从集合中删除并进行处理。 循环重复进行,直到集合中不包含任何任务。完整的示例请注意,必须为 System.Net.Http 添加引用。using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net.Http; using System.Threading;
异步编程场景解决耗时任务占用主线程执行的问题。因此耗时高的任务适用于异步编程,C#中有以下场景:对于 I/O 绑定代码,等待一个在 async 方法中返回 Task 或 Task<T> 的操作。对于 CPU 绑定代码,等待一个使用 Task.Run 方法在后台线程启动的操作。如何判断是CPU耗时操作还是IO耗时操作呢?简单来说,如果需要等待某些内容(如数据库中的数据、大文本文件的读取等),则为IO耗时操作;如果需要执行大量的计算,则属于CPU耗时操作。async awaitasync/await成对出现,用于简化异步编程。async出现在方法的声明前,表示这是一个异步方法。await则出现在这个异步方法的内部,一般在Task前。async/await关键字本身不会创建新线程,但被await的方法内部一般是通过创建线程来实现。微软官方给定的异步方法执行顺序:到底异步还是同步?这个标题算是个问题?问题又不太对。async/await成对出现的方法是异步方法,那哪来的同步呢?实际上最好先从多线程说起,关于异步编程这块,我准备整理一个系列出来。顺序上可能是这篇博客先出来,那就用下面的例子先展示一下。我看到有的文章说,async/await既可以是异步,又可以是同步。所以就会有疑惑,到底是同步还是异步呢?看下面两个例子。异步方式执行两个Task输出并没有按顺序,且并没有按主线程中代码顺序来执行。可以看到,两个异步方法尚未输出完成,主方法中main end.....已经输出。且Method1和Method2中的输出也是交替来的。输出结果:static void Main() { Console.WriteLine("main start...."); AsyncMethod1(); AsyncMethod2(); Thread.Sleep(1000); Console.WriteLine("main end....."); Console.ReadLine(); } static async void AsyncMethod1() { Console.WriteLine("start AsyncMethod1 async"); var result = await MyMethod1(); Console.WriteLine("end AsyncMethod1 async"); } static async Task<int> MyMethod1() { for(int i = 0; i < 5; i++) { Console.WriteLine("Async MyMethod1 start:" + i.ToString()); await Task.Delay(1000); } return 0; } static async void AsyncMethod2() { Console.WriteLine("start AsyncMethod2 async"); var result = await MyMethod2(); Console.WriteLine("end AsyncMethod2 async"); } static async Task<int> MyMethod2() { for(int i = 0; i < 5; i++) { Console.WriteLine("Async MyMethod2 start:" + i.ToString()); await Task.Delay(1000); } return 0; } 异步方法同步执行结合上面那个例子,再看这个例子的输出,就容易理解所谓的异步方法同步执行了。意思是说,Method1和Method2两个方法是异步执行的(理解成创建一个新线程去执行的吧)。但是,通过一些手段,让这两个任务按顺序来执行。即Method1执行完再来执行Method2。两个Task相对主线程来说,是异步去执行的,但这个异步也是按顺序来的。Method1输出完了,再执行Method2的输出static void Main() { Console.WriteLine("Async Test job"); Console.WriteLine("main start....."); Console.WriteLine("MyMethod() 异步方法同步执行:"); int i = MyMethod().GetAwaiter().GetResult(); int j = MyMethod2().GetAwaiter().GetResult(); Console.WriteLine($" i : {i}"); Console.WriteLine($" j : {j}"); Console.WriteLine("main end ....."); Console.ReadLine(); } static async Task<int> MyMethod() { for(int i = 0; i < 5; i++) { Console.WriteLine("Async Method start: " + i.ToString()); await Task.Delay(1000); } return 0; } static async Task<int> MyMethod2() { for(int i = 0; i < 5; i++) { Console.WriteLine("Async Method2 start: " + i.ToString()); await Task.Delay(1000); } return 0; } 何时使用async/awaitasp.net mvc中使用async/await并不能提升访问速度。但使用异步编程,可以提高响应能力(吞吐量),即使用异步方式在同一时间可以处理更多的请求。使用同步方式,线程会被耗时操作一直占用,直到耗时操作结束;使用异步方式,程序走到await关键字会立即return,释放线程,剩下的代码将放到一个回调(Task.GetAwaiter()的UnsafeOnCompleted(Action)),耗时操作完成时才会回调执行。在web服务器上, .NET Framework维护用于处理ASP.NET请求的线程池。当请求到达时,将调度线程池中的线程以处理该请求。如果以同步方式处理请求,则处理请求的线程将在处理请求时处于繁忙状态,并且该线程无法处理其他请求。在启动时看到大量并发请求的web应用中,或具有突发负载(其中并发增长突然增加)时,使web服务器调用异步会提高应用程序的相应能力。异步请求与同步请求所需的处理时间相同。如果请求发出需要两秒时间才能完成web服务调用,则该请求将需要两秒钟,无论是同步执行还是异步执行。但是,在异步调用期间,线程在等待第一个请求完成时不会被阻止响应其他请求。因此,当有多个并发请求调用长时间运行的操作时,异步请求会组织请求队列和线程池的增长。同步方式执行耗时任务 public ActionResult Index() { DateTime startTime = DateTime.Now;//进入DoSomething方法前的时间 var startThreadId = Thread.CurrentThread.ManagedThreadId;//进入DoSomething方法前的线程ID DoSomething();//耗时操作 DateTime endTime = DateTime.Now;//完成DoSomething方法的时间 var endThreadId = Thread.CurrentThread.ManagedThreadId;//完成DoSomething方法后的线程ID return Content($"startTime:{ startTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } startThreadId:{ startThreadId }\nendTime:{ endTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } endThreadId:{ endThreadId }"); } /// <summary> /// 耗时操作 /// </summary> /// <returns></returns> private void DoSomething() { Thread.Sleep(10000); } 异步方式执行耗时任务 public async Task<ActionResult> Index() { DateTime startTime = DateTime.Now;//进入DoSomething方法前的时间 var startThreadId = Thread.CurrentThread.ManagedThreadId;//进入DoSomething方法前的线程ID await DoSomething();//耗时操作 DateTime endTime = DateTime.Now;//完成DoSomething方法的时间 var endThreadId = Thread.CurrentThread.ManagedThreadId;//完成DoSomething方法后的线程ID return Content($"startTime:{ startTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } startThreadId:{ startThreadId }\nendTime:{ endTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } endThreadId:{ endThreadId }"); } /// <summary> /// 耗时操作 /// </summary> /// <returns></returns> private async Task DoSomething() { await Task.Run(() => Thread.Sleep(10000)); } 上面说了那么多,那到底该不该用async/await,什么时候用他俩?总结:对于计算密集型工作,使用多线程对于IO密集型工作,采用异步机制
1、什么是异步?异步操作通常用于执行完成时间可能较长的任务,如打开大文件、连接远程计算机或查询数据库=异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行。2、同步与异步的区别同步(Synchronous):在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行。异步(Asynchronous):在执行某个操作时,应用程序可在异步操作执行时继续执行。实质:异步操作,启动了新的线程,主线程与方法线程并行执行。3、异步和多线程的区别我们已经知道, 异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。简单的说就是:异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池。就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理。异步操作执行时,会将操作丢给线程池中的某个工作线程来完成。当开始I/O操作的时候,异步会将工作线程还给线程池,这意味着获取网页的工作不会再占用任何CPU资源了。直到异步完成,即获取网页完毕,异步才会通过回调的方式通知线程池。可见,异步模式借助于线程池,极大地节约了CPU的资源。注:DMA(Direct Memory Access)直接内存存取,顾名思义DMA功能就是让设备可以绕过处理器,直接由内存来读取资料。通过直接内存访问的数据交换几乎可以不损耗CPU的资源。在硬件中,硬盘、网卡、声卡、显卡等都有直接内存访问功能。异步编程模型就是让我们充分利用硬件的直接内存访问功能来释放CPU的压力。两者的应用场景:计算密集型工作,采用多线程。IO密集型工作,采用异步机制。C#异步代码参考(async and await)using System; using System.Threading; using System.Threading.Tasks; namespace AsyncAwaitDemo { class Program { static void Main(string[] args) { Console.WriteLine("main start.."); AsyncMethod(); Thread.Sleep(1000); Console.WriteLine("main end.."); Console.ReadLine(); } static async void AsyncMethod() { Console.WriteLine("start async"); var result = await MyMethod(); Console.WriteLine("end async"); //return 1; } static async Task<int> MyMethod() { for (int i = 0; i < 5; i++) { Console.WriteLine("Async start:" + i.ToString() + ".."); await Task.Delay(1000); //模拟耗时操作 } return 0; } } } 使用Wait()和GetAwaiter().GetResult()方法实现异步方法同步执行using System; using System.Threading.Tasks; namespace AsyncTest { class Program { static void Main(string[] args) { Console.WriteLine("Async Test job:"); Console.WriteLine("main start.."); Console.WriteLine("MyMethod()异步方法同步执行:"); MyMethod().Wait();//在main中等待async方法执行完成 int i = MyMethod().GetAwaiter().GetResult();//用于在main中同步获取async方法的返回结果 Console.WriteLine("i:" + i); Console.WriteLine("main end.."); Console.ReadKey(true); } static async Task<int> MyMethod() { for (int i = 0; i < 5; i++) { Console.WriteLine("Async start:" + i.ToString() + ".."); await Task.Delay(1000); //模拟耗时操作 } return 0; } } } C# 异步编程 异步委托调用同步方法同步化操作:由前后紧接的组件或函数调用组成。一个同步化调用会阻塞整个进程直到这一个操作完成。异步化操作:不会阻塞启动操作的调用线程。调用程序必须通过轮流检测、软件中的中断信号或只是明确地等待完成信号来发现调用的完成。.NET 为异步操作提供两种设计模式:使用 IAsyncResult 对象的异步操作。使用事件的异步操作。.NET的许多方面都支持异步编程功能,这些方面包括:文件 IO、流 IO、套接字 IO。网络。远程处理信道(HTTP、TCP)和代理。使用 ASP.NET 创建的 XML Web services。ASP.NET Web 窗体。使用 MessageQueue 类的消息队列。异步委托提供以异步方式调用同步方法的能力。当同步调用一个委托时,调用方法直接对当前线程调用目标方法。如果编译器支持异步委托,则它将生成该调用方法以及BeginInvoke 和EndInvoke 方法。如果调用BeginInvoke方法,则公共语言运行库将对请求进行排队并立即返回到调用方。将对来自线程池的线程调用该目标方法。提交请求的原始线程自由地继续与目标方法并行执行,该目标方法是对线程池线程运行的在回调中,使用EndInvoke 方法来获取返回值和输入/输出参数。如果没有对BeginInvoke指定回调,则可以在提交请求的原始线程上使用EndInvoke。using System; using System.Threading; using System.Runtime.Remoting.Messaging; namespace ClassMain { //委托声明(函数签名) delegate string MyMethodDelegate(); class MyClass { //要调用的动态方法 public string MyMethod1() { return "Hello Word1"; }
在面向对象编程中,经常会面对创建对象和销毁对象的情况,如果不正确处理的话,在短时间内创建大量对象然后执行简单处理之后又要销毁这些刚刚建立的对象,这是一个非常消耗性能的低效行为,所以很多面向对象语言中在内部使用对象池来处理这种情况,以提高性能,比如在ADO.NET内部就允许使用数据库连接池来提高性能,在JDBC中没有提供数据库连接池,一些开发人员为了提高效率就自己编写数据库连接池来提高性能,当然据我所知在Java中有些框架提供了数据库连接的池化处理。在多线程编程时也会遇到上面的情况,如果创建了过多的线程将会增加操作系统资源的占用,并且还要处理资源要求和潜在的占用冲突,并且使用了多线程之后将使代码的执行流程和资源竞争情况变得复杂,稍不留心就会产生bug(在第二篇中在我写的代码中就曾经出现过一个bug,后来我自己发现并处理了这个bug)。在使用多线程编程时对需要同步的资源访问尤其需要注意,如系统资源(系统端口等)、共享资源(文件、窗口句柄等)、属于单个应用程序的资源(如全局、静态和实例字段或属性)。针对上面的情况,我们可以使用线程池来解决上面的大部分问题,跟使用单个线程相比,使用线程池有如下优点:1、缩短应用程序的响应时间。因为在线程池中有线程的线程处于等待分配任务状态(只要没有超过线程池的最大上限),无需创建线程。2、不必管理和维护生存周期短暂的线程,不用在创建时为其分配资源,在其执行完任务之后释放资源。3、线程池会根据当前系统特点对池内的线程进行优化处理。总之使用线程池的作用就是减少创建和销毁线程的系统开销。在.NET中有一个线程的类ThreadPool,它提供了线程池的管理。ThreadPool是一个静态类,它没有构造函数,对外提供的函数也全部是静态的。其中有一个QueueUserWorkItem方法,它有两种重载形式,如下:public static bool QueueUserWorkItem(WaitCallback callBack):将方法排入队列以便执行。此方法在有线程池线程变得可用时执行。public static bool QueueUserWorkItem(WaitCallback callBack,Object state):将方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。QueueUserWorkItem方法中使用的的WaitCallback参数表示一个delegate,它的声明如下:public delegate void WaitCallback(Object state)如果需要传递任务信息可以利用WaitCallback中的state参数,类似于ParameterizedThreadStart委托。下面是一个ThreadPool的例子,代码如下:using System.Threading; using System.Collections; using System.Diagnostics; using System; using System.ComponentModel; namespace ThreadPoolDemo { class ThreadPoolDemo1 { public ThreadPoolDemo1() { } public void Work() { ThreadPool.QueueUserWorkItem(new WaitCallback(CountProcess)); ThreadPool.QueueUserWorkItem(new WaitCallback(GetEnvironmentVariables)); } /// <summary> /// 统计当前正在运行的系统进程信息 /// </summary> /// <param name="state"></param> private void CountProcess(object state) { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { try { Console.WriteLine("Id:{0},ProcessName:{1},StartTime:{2}", p.Id, p.ProcessName, p.StartTime); } catch (Win32Exception e) { Console.WriteLine("ProcessName:{0}", p.ProcessName); } finally { } } Console.WriteLine("获取进程信息完毕。"); } /// <summary> /// 获取当前机器系统变量设置 /// </summary> /// <param name="state"></param> public void GetEnvironmentVariables(object state) { IDictionary list=System.Environment.GetEnvironmentVariables(); foreach (DictionaryEntry item in list) { Console.WriteLine("key={0},value={1}", item.Key, item.Value); } Console.WriteLine("获取系统变量信息完毕。"); } static void Main(string[] args) { ThreadPoolDemo1 tpd1 = new ThreadPoolDemo1(); tpd1.Work(); Thread.Sleep(5000); Console.WriteLine("OK"); Console.ReadLine(); } } }上面这段代码在本机的运行情况如下:key=Path,value=C:/WINDOWS/system32;C:/WINDOWS;C:/WINDOWS/System32/Wbem;C:/Program Files/Microsoft SQL Server/80/Tools/BINN;C:/Program Files/Microsoft SQL Server/80/Tools/Binn/;C:/Program Files/Microsoft SQL Server/90/DTS/Binn/;C:/Program Files/Microsoft SQL Server/90/Tools/binn/;C:/Program Files/Microsoft SQL Server/90/Tools/Binn/VSShell/Common7/IDE/;C:/Program Files/Microsoft Visual Studio 8/Common7/IDE/PrivateAssemblies/;C:/MySQL Server 5.1/bin;C:/PHP-5.2.9-Win32 key=TEMP,value=C:/DOCUME~1/ZHOUFO~1/LOCALS~1/Temp key=SESSIONNAME,value=Console key=PATHEXT,value=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.js;.JSE;.WSF;.WSH key=Rav,value=C:/Documents and Settings/All Users/Application Data/Rising/Rav key=PROCESSOR_ARCHITECTURE,value=x86 key=SystemDrive,value=C: key=APPDATA,value=C:/Documents and Settings/zhoufoxcn/Application Data key=windir,value=C:/WINDOWS key=USERPROFILE,value=C:/Documents and Settings/zhoufoxcn key=TMP,value=C:/DOCUME~1/ZHOUFO~1/LOCALS~1/Temp key=USERDOMAIN,value=ZHOU key=ProgramFiles,value=C:/Program Files key=FP_NO_HOST_CHECK,value=NO key=HOMEPATH,value=/Documents and Settings/zhoufoxcn key=COMPUTERNAME,value=ZHOU key=USERNAME,value=zhoufoxcn key=NUMBER_OF_PROCESSORS,value=2 key=PROCESSOR_IDENTIFIER,value=x86 Family 6 Model 15 Stepping 13, GenuineIntel key=SystemRoot,value=C:/WINDOWS key=ComSpec,value=C:/WINDOWS/system32/cmd.exe key=LOGONSERVER,value=//ZHOU key=CommonProgramFiles,value=C:/Program Files/Common Files key=PROMPT,value=$P$G key=PROCESSOR_LEVEL,value=6 key=PROCESSOR_REVISION,value=0f0d key=VS80COMNTOOLS,value=C:/Program Files/Microsoft Visual Studio 8/Common7/Tools/ key=lib,value=C:/Program Files/SQLXML 4.0/bin/ key=ALLUSERSPROFILE,value=C:/Documents and Settings/All Users key=VS90COMNTOOLS,value=c:/Program Files/Microsoft Visual Studio 9.0/Common7/Tools/ key=OS,value=Windows_NT key=HOMEDRIVE,value=C: 获取系统变量信息完毕。 Id:1864,ProcessName:mysqld,StartTime:2010-1-11 8:23:25 Id:3732,ProcessName:HTime,StartTime:2010-1-11 8:32:16 Id:1328,ProcessName:spoolsv,StartTime:2010-1-11 8:23:23 Id:3908,ProcessName:ctfmon,StartTime:2010-1-11 8:32:19 Id:3640,ProcessName:cmd,StartTime:2010-1-11 15:43:04 Id:1488,ProcessName:notepad,StartTime:2010-1-11 15:30:48 Id:3668,ProcessName:conime,StartTime:2010-1-11 15:27:22 Id:964,ProcessName:svchost,StartTime:2010-1-11 8:23:22 Id:1408,ProcessName:svchost,StartTime:2010-1-11 8:23:23 Id:1140,ProcessName:svchost,StartTime:2010-1-11 8:23:22 Id:1940,ProcessName:sqlbrowser,StartTime:2010-1-11 8:23:25 Id:1672,ProcessName:MsDtsSrvr,StartTime:2010-1-11 8:23:24 Id:3540,ProcessName:explorer,StartTime:2010-1-11 8:32:14 Id:3268,ProcessName:wps,StartTime:2010-1-11 14:08:00 Id:1568,ProcessName:inetinfo,StartTime:2010-1-11 8:23:24 Id:588,ProcessName:csrss,StartTime:2010-1-11 8:23:17 Id:3704,ProcessName:360tray,StartTime:2010-1-11 8:32:16 Id:1028,ProcessName:svchost,StartTime:2010-1-11 8:23:22 Id:2184,ProcessName:mqtgsvc,StartTime:2010-1-11 8:23:28 Id:2628,ProcessName:Reflector,StartTime:2010-1-11 15:04:35 Id:3872,ProcessName:devenv,StartTime:2010-1-11 15:26:51 Id:204,ProcessName:ThreadPoolDemo,StartTime:2010-1-11 15:43:26 Id:664,ProcessName:winlogon,StartTime:2010-1-11 8:23:20 Id:840,ProcessName:dexplore,StartTime:2010-1-11 14:50:21 Id:3900,ProcessName:rundll32,StartTime:2010-1-11 8:32:18 Id:1636,ProcessName:mdm,StartTime:2010-1-11 8:23:24 Id:1012,ProcessName:RavMonD,StartTime:2010-1-11 8:23:22 Id:1100,ProcessName:svchost,StartTime:2010-1-11 8:23:22 Id:476,ProcessName:smss,StartTime:2010-1-11 8:23:14 Id:920,ProcessName:svchost,StartTime:2010-1-11 8:23:21 Id:716,ProcessName:services,StartTime:2010-1-11 8:23:21 Id:3052,ProcessName:ThreadPoolDemo.vshost,StartTime:2010-1-11 15:42:27 Id:1448,ProcessName:msdtc,StartTime:2010-1-11 8:23:23 Id:180,ProcessName:mqsvc,StartTime:2010-1-11 8:23:25 Id:2512,ProcessName:iexplore,StartTime:2010-1-11 14:52:29 Id:1888,ProcessName:nvsvc32,StartTime:2010-1-11 8:23:25 Id:728,ProcessName:lsass,StartTime:2010-1-11 8:23:21 Id:2240,ProcessName:alg,StartTime:2010-1-11 8:23:28 Id:3808,ProcessName:jusched,StartTime:2010-1-11 8:32:17 Id:3128,ProcessName:RsTray,StartTime:2010-1-11 8:32:17 Id:1992,ProcessName:svchost,StartTime:2010-1-11 8:23:25 Id:2944,ProcessName:Foxit Reader,StartTime:2010-1-11 14:08:48 Id:4,ProcessName:System,StartTime:1601-1-1 8:00:00 Id:1604,ProcessName:jqs,StartTime:2010-1-11 8:23:24 ProcessName:Idle 获取进程信息完毕。在上面的代码中我们使用了线程池,并让它执行了两个任务,一个是列出系统当前所有环境变量的值,一个是列出系统当前运行的进程名和它们的启动时间。当然,优点和缺点总是同时存在的,使用ThreadPool也有一些缺点,使用线程池有如下缺点:1、一旦加入到线程池中就没有办法让它停止,除非任务执行完毕自动停止;2、一个进程共享一个线程池;3、要执行的任务不能有返回值(当然,线程中要执行的方法也是不能有返回值,如果确实需要返回值必须采用其它技巧来解决);4、在线程池中所有任务的优先级都是一样的,无法设置任务的优先级;5、不太适合需要长期执行的任务(比如在Windows服务中执行),也不适合大的任务;6、不能为线程设置稳定的关联标识,比如为线程池中执行某个特定任务的线程指定名称或者其它属性。如果我们要面临的情况正好是线程池的缺点,那么我们只好继续使用线程而不是线程池。不过在某些情况下使用线程池确实可以带来很多方便的,比如在WEB服务器中,可以使用线程池来处理来自客户端的请求,可以以比较高的性能运行。
使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。 如果对方法或表达式使用此修饰符,则其称为异步方法。public async Task<int> ExampleMethodAsync() { // . . . . }如果你不熟悉异步编程或不了解异步方法如何使用 await 关键字来完成可能需要长时间运行的工作而不阻止调用方的线程,请阅读使用 async 和 await 的异步编程中的简介。string contents = await contentsTask;方法将同步运行,直至到达其第一个 await 表达式,此时会将方法挂起,直到等待的任务完成。 同时,如下节示例中所示,控件将返回到方法的调用方。如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 的任何异步方法,因为该情况可能表示存在错误。 请参阅编译器警告(等级 1)CS4014。async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。示例下面的示例展示了异步事件处理程序 StartButton_Click 和异步方法 ExampleMethodAsync 之间的控制结构和流程。 此异步方法得到的结果是一个下载网站的长度。 此代码适用于在 Visual Studio 中创建的 Windows Presentation Foundation (WPF) 应用或 Windows 应用商店应用;请参见有关设置应用的代码注释。private async void StartButton_Click(object sender, RoutedEventArgs e) { ResultsTextBox.Text += "\n"; try { int length = await ExampleMethodAsync(); ResultsTextBox.Text += String.Format("Length: {0}\n", length); } catch (Exception) { } } public async Task<int> ExampleMethodAsync() { var httpClient = new HttpClient(); int exampleInt = (await httpClient.GetStringAsync("http://msdn.microsoft.com")).Length; ResultsTextBox.Text += "Preparing to finish ExampleMethodAsync.\n"; return exampleInt; } // Output: // Preparing to finish ExampleMethodAsync. // Length: 53292 返回类型异步方法的返回类型可为 Task、Task<TResult> 或 void。 方法不能声明任何 ref 或 out 参数,但是可以调用具有这类参数的方法。如果异步方法的 语句指定一个 类型的操作数,则应指定 Task<TResult> 作为方法的返回类型TResult。 如果当方法完成时未返回有意义的值,则应使用 Task。 即,对方法的调用将返回一个 Task,但是当 Task 完成时,任何等待 await 的所有 Task 表达式的计算结果都为 void。你应主要使用 void 返回类型来定义事件处理程序,这些处理程序需要此返回类型。 void 返回异步方法的调用方不能等待,并且无法捕获该方法引发的异常。await 运算符在异步方法应用于任务,以挂起执行方法,直到所等待的任务完成。 任务表示正在进行的工作。在其中使用 await 的异步方法必须通过 async 关键字进行修改。 使用 async 修饰符定义并且通常包含一个或多个 await 表达式的这类方法称为异步方法。应用 await 运算符的任务通常是实现基于任务的异步模式的方法调用的返回值。 示例包括类型 Task 或 Task<TResult> 的值。在下列代码中,HttpClient 方法GetByteArrayAsync 返回 Task\<byte[]> 和 getContentsTask。 当任务完成时,任务约定生成实际字节数组。 await 运算符应用于 getContentsTask 以在 SumPageSizesAsync 中挂起执行,直到 getContentsTask 完成。 同时,控制权会返回给 SumPageSizesAsync 的调用方。 当 getContentsTask 完成之后,await 表达式计算为字节数组。private async Task SumPageSizesAsync() { //要在桌面应用程序中使用HttpClient类型,您必须包含using指令,并为System.Net.Http命名空间添加引用。 HttpClient client = new HttpClient(); // . . . Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); byte[] urlContents = await getContentsTask; //byte[] urlContents = await client.GetByteArrayAsync(url); // . . . }如前面的示例中所示,如果 await 应用于返回 Task<TResult> 的方法调用结果,则 await 表达式的类型为 TResult。 如果 await 应用于返回 Task 的方法调用结果,则 await 表达式的类型为 void。 以下示例演示了差异。// 关键字await用于 返回任务<TResult>的方法 TResult result = await AsyncMethodThatReturnsTaskTResult(); //关键字await与返回任务的方法一起使用 await AsyncMethodThatReturnsTask();await 表达式不阻止正在执行它的线程。 而是使编译器将剩下的异步方法注册为等待任务的延续任务。 控制权随后会返回给异步方法的调用方。 任务完成时,它会调用其延续任务,异步方法的执行会在暂停的位置处恢复。await 表达式只能在由 async 修饰符标记的立即封闭方法体、lambda 表达式或异步方法中出现。 术语 await 在该上下文中仅用作关键字。 在其他位置,它会解释为标识符。 在方法、Lambda 表达式或匿名方法中,await 表达式不能在同步函数体、查询表达式、lock 语句块或不安全上下文中出现。异常大多数异步方法将返回 Task 或 Task<TResult>。 返回任务的属性携带有关其状态和历史记录的信息,如任务是否完成、异步方法是否导致异常或已取消以及最终结果是什么。 await 运算符可访问这些属性。如果等待的任务返回异步方法导致异常,则 await 运算符会重新引发异常。如果等待的任务返回异步方法取消,则 await 运算符会重新引发 OperationCanceledException。处于故障状态的单个任务可以反映多个异常。 例如,任务可能是调用 Task.WhenAll 的结果。 等待此类任务时,等待操作仅重新引发异常之一。 但是,无法预测重新引发的异常。有关异步方法中的错误处理的示例,请参阅 try catch。示例下面的 Windows 窗体示例阐释如何在异步方法 WaitAsynchronouslyAsync 中使用 await。 将该方法的行为与 WaitSynchronously 的行为进行对比。 如果未向任务应用 await 运算符,WaitSynchronously 就会同步运行,而不管其定义中是否使用了 async 修饰符和在主体中是否调用了 Thread.Sleep。private async void button1_Click(object sender, EventArgs e) { //调用异步运行的方法 string result = await WaitAsynchronouslyAsync(); //调用同步运行的方法 //string result = await WaitSynchronously (); //显示结果 textBox1.Text += result; } //以下方法异步运行。 延迟期间UI线程不被阻止。 您可以在Task.Delay运行时移动或调整Form1窗口大小。 public async Task<string> WaitAsynchronouslyAsync() { await Task.Delay(10000); return "Finished"; } //尽管使用异步,但以下方法同步运行。 //由于UI线程被阻止,因此Thread.Sleep正在运行时,无法移动或调整Form1窗口的大小。 public async Task<string> WaitSynchronously() { //为System.Threading添加一个using指令 Thread.Sleep(10000); return "Finished"; }
如果可以并行可以大大提高性能,但在我们的使用中,不可能全是并行的也是要有线行操作,所以我们需要在业务逻辑层进行并行操作的护展:数据访问层不变还是以前一样如下:public class UserDAL { public User GetUser() { User user = new User(); user.Name = "N1"; user.Address = "A1"; return user; } public List<User> GetUserList() { List<User> list = new List<User>(); list.Add(new User { Name = "N11", Address = "A11" }); list.Add(new User { Name = "N12", Address = "A12" }); return list; } } 业务逻辑层需要护展有async,如下:public class UserBLL { UserDAL dal = new UserDAL(); public async Task<User> GetUserAsync() { return await Task.Run(() => { return dal.GetUser(); }); } public User GetUser() { return dal.GetUser(); } public async Task<List<User>> GetUserListAsync() { return await Task.Run(() => { return dal.GetUserList(); }); } public List<User> GetUserList() { return dal.GetUserList(); } } 最后是调用了,如下:UserBLL userBLL = new UserBLL(); public async Task<ActionResult> Index() { var user = userBLL.GetUserAsync(); var listUser = userBLL.GetUserListAsync(); int t1 = Environment.TickCount; await Task.WhenAll(user, listUser); ViewBag.User = user.Result; ViewBag.ListUser = listUser.Result; //ViewBag.User = await user; //ViewBag.ListUser = await listUser; ViewBag.Times = Environment.TickCount - t1; return View(); } 具体的性能我们可以测试,在数据访问层每次延时500毫秒,用并行和线行测试一下,并行大约时间在500毫秒左右,而线行则在1000毫秒左右C#异步编程再续 async异步方法与同步方法的并行今天晚上没事写了个测试的代码,又看了看.net的并行编程,两个方法,一个是异步async修饰的,另外一个是普通的方法,在控制台程序的Main方法里去调用这两个方法,会有什么结果呢?编程首先咱们看一下方法的组成,step1以下异步 public async void Step1() { try { //await进行等待后,新线程的异常能够被主线程捕捉,这是正常的,下面的代码不会被执行 await Task.Run(() => { Console.WriteLine("Step1 Current ThreadID" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); }); await Task.Run(() => { Console.WriteLine("Step1 Current ThreadID" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("ThreadTest.Test Runing"); }); } catch (Exception ex) { Console.WriteLine("ThreadTest" + ex.Message); } } step2以下async public void Step2() { Console.WriteLine("Step2 Current ThreadID" + Thread.CurrentThread.ManagedThreadId); } 咱们能够看到step2很简单,不会有什么延时,就是在屏幕上输出一段话,而step1就显得复杂一些了,它是一个异步的方法,而且使用Task.Run开启了两个新线程,而第一测试个线程的运行时间是3秒,很长呀,哈哈,第二个是在屏幕上输出一段话!如今咱们把step1和step2写在一块儿会出现什么状况呢?spa var test = new ThreadTest(); test.Step1();//整个方法不阻塞,但方法内部有可能阻塞 test.Step2(); 经过上面的图咱们认识到了,step1按着顺序先执行,而因为第一个线程要执行3秒,这时step2被并行执行,3秒后,step1的第二个线程继续执行(因为使用了await,因此step1内部进行了等待,不会应响他外面的方法,也应响不了,呵呵!.net怎么了,看了上面的例如,是否是对并行编程有了新的认识呢!线程C# 同步转异步 TaskCompletionSource当我们遇到一些异步执行又无法等待时的逻辑,比如动画的执行。而业务上又需要等待逻辑的完成,再去处理后续的操作。这时需要转成异步方法如下,同步执行一个动画后,再输出日志:private async void TaskCompleteSourceAwait_OnClick(object sender, RoutedEventArgs e) { bool isCompleted = await AwaitByTaskCompletionAsync(new Storyboard() { Duration = new Duration(TimeSpan.FromSeconds(2)) }); Debug.WriteLine($"TaskCompleteSourceAwait_OnClick end:{isCompleted}"); } 通过TaskCompletionSource如何转化为异步方法,并等待动画完成?/// <summary> /// 执行动画 /// </summary> /// <param name="storyboard"></param> /// <returns></returns> public static async Task<bool> AwaitByTaskCompletionAsync(Storyboard storyboard) { var taskCompletionSource = new TaskCompletionSource<bool>(); Debug.WriteLine("Storyboard start"); storyboard.Completed += OnStoryboardCompleted; storyboard.Begin(); void OnStoryboardCompleted(object sender, EventArgs e) { Debug.WriteLine("Storyboard end"); storyboard.Completed -= OnStoryboardCompleted; taskCompletionSource.SetResult(true); } return await taskCompletionSource.Task; } 测试效果: