我是异步编程的新手,并尝试编写一些测试代码以查看我可以用它做什么。
下面是我的测试控制台应用代码,调用异步方法,该方法会将一些现有的.docx文件发布到我的 Web API 并转换为 PDF。
static void Main(string[] args)
{
//async testing - 5 files batch
Console.WriteLine("Job Start:");
for (int i = 0; i < 5; i++)
{
var filestream = File.Open(@"c:pdftest" + i.ToString() +@".docx", FileMode.Open);
//delegate
Action act = async () =>
{
await test(filestream, i.ToString());
filestream.Dispose();
};
act();
}
Console.WriteLine("End.");
Console.ReadLine();
}
和方法:
static async Task<int> test(FileStream fs, string id)
{
var content = new StreamContent(fs);
var client = new HttpClient();
var startTime = DateTime.Now;
//post to web api
var response = await client.PostAsync("http://localhost:50348/PDFConvert/Convert?name=" + id, content);
var ts = DateTime.Now - startTime;
Console.WriteLine("Time Cost: " + ts.TotalSeconds.ToString() + " seconds, " + response.Content.ReadAsStringAsync().Result.Replace(""", ""));
client.Dispose();
return 0;
}
到目前为止,它可以工作,但一个问题是控制台窗口上"作业开始:"之后打印的"结束",然后等待输出。
所以我的问题是:
1.如何在所有异步/等待完成后打印"结束"消息?
2.不确定这是否是最佳实践,那么对现有代码有什么建议吗?
多谢。
你正在编写事件处理程序,并且如果你强制它是一个导致async void
委托的Action
委托,否则你永远不应该async void
这样做。
将您的委托更改为Func<Task>
然后,您可以获取任务数组并对其进行WaitAll
。
static void Main(string[] args)
{
//async testing - 5 files batch
Console.WriteLine("Job Start:");
var tasks = new Task[5];
for (int i = 0; i < 5; i++)
{
var filestream = File.Open(@"c:pdftest" + i.ToString() +@".docx", FileMode.Open);
//Moved i.ToString() out of the delegate to fix a potential bug with variable capture.
var stringToPrint = i.ToString()
//delegate
Func<Task> act = async () =>
{
await test(filestream, stringToPrint);
filestream.Dispose();
};
var task = act();
tasks[i] = task;
}
//In a non console program you will likely want to do "await Task.WhenAll(tasks);" instead.
Task.WaitAll(tasks);
Console.WriteLine("End.");
Console.ReadLine();
}
我是异步编程的新手
我建议先读async
介绍,然后再写async
最佳实践文章。
不要将 lambda 与Action
委托类型一起使用async
;这会导致方法async void
。async void
方法的缺点之一是您无法轻松判断它们何时完成。
异步不会自然地与控制台应用相吻合。您可以在异步代码上阻止主线程,但这不是在任何类型的其他应用程序中使用的好模式:
static void Main(string[] args)
{
Console.WriteLine("Job Start:");
MainAsync().GetAwaiter().GetResult();
Console.WriteLine("End.");
Console.ReadLine();
}
static async Task MainAsync()
{
for (int i = 0; i < 5; i++)
{
using (var filestream = File.Open(@"c:pdftest" + i.ToString() +@".docx", FileMode.Open))
await test(filestream, i.ToString());
}
}
因评论而更新:
由于您想同时运行任务,因此您应该按照我async
介绍文章中的指导并使用Task.WhenAll
:
static async Task MainAsync()
{
var tasks = Enumerable.Range(0, 5).Select(TestFilesAsync).ToList();
await Task.WhenAll(tasks);
}
static async Task TestFilesAsync(int i)
{
using (var filestream = File.Open(@"c:pdftest" + i.ToString() +@".docx", FileMode.Open))
await test(filestream, i.ToString());
}
您可以在 Linq Select 方法中使用异步 lambda 来迭代任务列表并等待它们完成
var list = Enumerable.Range(1,5).Select (
async i => {
var filestream = File.Open(@"c:pdftest" + i.ToString() +@".docx", FileMode.Open);
await test(filestream, i.ToString());
filestream.Dispose();
}
);
Task.WaitAll (list.ToArray ());
如果它不是控制台应用程序而是异步上下文,则可以使用 WhenAll
而不是 WaitAll
。