【发布时间】:2017-01-16 19:20:53
【问题描述】:
我经常在网上看到 async\await 是编程中的“天才创新”。有时是这样,但在某些情况下,我觉得它不能缩短需要编写的代码。
如果我需要 3 个并行任务(下载)并且我想对每次下载的结果进行处理(其中处理(将 SUM 输出到控制台)取决于每次下载,并且我同时需要所有结果,如 #1 所示),我可以使用 Thread 来完成,而无需像这里那样使用 async/await(一些伪代码,因为 GetByteArray 不存在):
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static int length = 0;
static HttpClient client =
new HttpClient() { MaxResponseContentBufferSize = 1000000 };
static void Main(string[] args)
{
CreateMultipleTasksAsync();
Console.ReadKey();
}
static void CreateMultipleTasksAsync()
{
Thread th1 = new Thread(ProcessURLAsync);
th1.Start("http://msdn.microsoft.com");
Thread th2 = new Thread(ProcessURLAsync);
th2.Start("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx");
Thread th3 = new Thread(ProcessURLAsync);
th3.Start("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx");
//#2 there I need results of all 3 downloads (so it will wait for all 3 downloads being completed)
Console.WriteLine("\r\n\r\nTotal bytes returned: {0}\r\n", Program.length);
}
static void ProcessURLAsync(object urlObj)
{
string url = (string)urlObj;
var length = client.GetByteArray(url).Length;
/* //#1 there I need only a result of one current download (so it will wait only for current download being completed)
Console.WriteLine("\n{0,-58} {1}", url, length);*/
Program.length =+ length;
}
}
}
或者可以使用 async/await 这样做:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
CreateMultipleTasksAsync();
Console.ReadKey();
}
static async Task CreateMultipleTasksAsync()
{
HttpClient client =
new HttpClient() { MaxResponseContentBufferSize = 1000000 };
Task<byte[]> download1 =
client.GetByteArrayAsync("http://msdn.microsoft.com");
Task<byte[]> download2 =
client.GetByteArrayAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx");
Task<byte[]> download3 =
client.GetByteArrayAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx");
int length1 = (await download1).Length;
int length2 = (await download2).Length;
int length3 = (await download3).Length;
//#1 there I need results of all 3 downloads (so it will wait for all 3 downloads being completed)
Console.WriteLine("\r\n\r\nTotal bytes returned: {0}\r\n", length1 + length2 + length3);
}
}
}
而且 async/await 方式确实更短更好,因为我所有的代码都很紧凑,而且都在一个方法中 CreateMultipleTasksAsync,我不应该创建额外的方法并委托它。
但是,如果我想对单个下载的结果做一些事情(独立于其他下载,如 #2 所示),我需要将一段代码取出到一个单独的方法中,其中只有一个 await 修饰符方法。
我需要额外的方法,因为我不能这样写代码:
...
Task<int> download1 =
ProcessURLAsync("http://msdn.microsoft.com", client);
Task<int> download2 =
ProcessURLAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client);
Task<int> download3 =
ProcessURLAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client);
int length1 = (await download1).Length;
//#3
Console.WriteLine("\n{0,-58} {1}", "http://msdn.microsoft.com", length1);
int length2 = (await download2).Length;
//#4
Console.WriteLine("\n{0,-58} {1}", "http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", length2);
int length3 = (await download3).Length;
//#5
...
因为在此代码中,总是在处理 download1 (#3) 的结果后处理 download2 的结果(在 #4 处输出到控制台),并且在处理后始终处理 download3 的结果(在 #5 处输出到控制台) download1 (#3) 和 download2 (#4) 的结果(不同结果的处理依赖于其他结果的处理)。并且这种情况下的输出顺序总是相同的(即使 download3 比 download1 更早完成,无论如何 #5 会在 #3 之后显示)。
但是如果我希望 #5 显示在 #3 之前,而 download3 比 download1 更早完成,我不得不创建一个额外的方法 ProcessURLAsync:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
CreateMultipleTasksAsync();
Console.ReadKey();
}
static async Task CreateMultipleTasksAsync()
{
HttpClient client =
new HttpClient() { MaxResponseContentBufferSize = 1000000 };
Task<int> download1 =
ProcessURLAsync("http://msdn.microsoft.com", client);
Task<int> download2 =
ProcessURLAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client);
Task<int> download3 =
ProcessURLAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client);
int length1 = await download1;
int length2 = await download2;
int length3 = await download3;
//#1 there I need results of all 3 downloads (so it will wait for all 3 downloads being completed)
Console.WriteLine("\r\n\r\nTotal bytes returned: {0}\r\n", length1 + length2 + length3);
}
static async Task<int> ProcessURLAsync(string url, HttpClient client)
{
var length = (await client.GetByteArrayAsync(url)).Length;
//#2 there I need only a result of one current download, independently from other downloads (so it will wait only for current download being completed)
Console.WriteLine("\n{0,-58} {1}", url, length);
return length;
}
}
}
在这种情况下,为什么 async/await 方式更好并不那么明显,因为我需要创建额外的方法 ProcessURLAsync 来处理单个下载的结果,所以我的代码很像第一个示例的代码,没有异步/等待。
如果我们忽略 async/await-way 明显优于 Thread-way 的事实有 3 个原因 - 1)您应该将 url 作为对象传递给委托,然后将其转换回字符串,2)您只能使用用于存储长度(length1、length2、length3)的局部变量,无需创建静态属性 Program.length(因为您不能从委托返回值),3)您不能将超过 1 个参数传递给委托所以你需要使'HttpClient客户端'静态而不是本地 - 这是否意味着异步/等待方式只有在以下情况下才真正更好:1)你只需要1个并行任务; 2)或者您需要多个并行任务但不需要彼此分开处理(只需要完全处理它们)?
如果没有一个并行任务相互依赖,我应该分别处理它们,那么语法 async/await 有什么优势吗?
【问题讨论】:
-
大锤比锤子好吗?视情况而定。不要忘记任务不是线程。 blogs.msdn.microsoft.com/benwilli/2015/09/10/…
-
请edit您的帖子并提出具体问题。目前还不清楚您想要实现什么...目前您正在比较不正确的(不是线程安全的)代码与并行运行的线程以及对多个异步任务的顺序调用 - 并声称没有理由执行任务...相当令人困惑。
-
并发线程和等待异步操作是不同的东西。你到底在比较什么?
标签: c# .net multithreading asynchronous async-await