我有一个有两个角色的WinForms应用程序。如果不存在命令行参数,则Main函数调用Application.Run,并显示UI。如果存在命令行参数,则不调用Application.Run。相反,我调用这样的异步方法:
result = HandleCommandLine(args).GetAwaiter().GetResult();
(我是async / await的新手,此表格基于SO答案)。
最终目标是遍历列表,并为每个条目启动一个新任务。这些任务中的每一项都应与其他任务并行运行。任务开始如下:
runningTasks.Add(Task.Factory.StartNew((args) => HandlePlayback( (Dictionary<string,string>) ((object[])args)[0]), new object[] { runArgs } ));
任务被添加到runningTasks的集合中,我稍后调用:
Task.WaitAll(runningTasks.ToArray());
在每个runningTasks中,我正在尝试使用HttpClient发送Web请求:
using (HttpResponseMessage response = await Client.SendAsync(message))
{
using (HttpContent responseContent = response.Content)
{
result = await responseContent.ReadAsStringAsync();
}
}
一旦调用Client.SendAsync,整个事情就会变得糟透了。我的所有runningTasks都完成了,应用程序退出了。在任何这些任务中都没有经过Client.SendAsync执行任何操作。
由于我是async / await的新手,因此我对完全可能出错的内容几乎没有什么想法,因此很少有关于如何修复它的想法。我想这与在这种情况下的SynchronizationContexts有关(WinForms应用程序就像一个控制台应用程序),但我没有理解我需要做什么以及在哪里保持服务请求和Web请求异步调用来自导致一切都过早完成。
我想我的问题是,为什么(只有一些)等待&#39;等等。调用导致所有任务完成?我该怎么办呢?
更新
两件事。 @Joe White:无论我在哪里检查,WindowsFormsSynchronizationContext.Current都是null。
@David Pine:Minimal(种类:))完整可行的例子如下。您将需要向项目添加命令行参数,或强制执行HandleCommandLine函数。在此示例中,它尝试为三个站点中的每个站点发出网站请求。如果它们存在,它似乎并不重要。代码到达Client.SendAsync一定次数(通常不是三次),但时间似乎很重要。
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
static List<Task> runningTasks = new List<Task>();
[STAThread]
static int Main()
{
int result = 1; // true, optimism
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
if (args.Length > 1)
{
// do the command line work async, while keeping this thread active
result = HandleCommandLine(args).GetAwaiter().GetResult();
}
else
{
// normal interface mode
Application.Run(new Form1());
}
return result;
}
static async Task<int> HandleCommandLine(string[] args)
{
// headless mode
int result = 1; // true, optimism
result = await HandleControlMode(args);
return result;
}
private static async Task<int> HandleControlMode(string[] Arguments)
{
int result = 1; // optimism
try
{
List<string> sites = new List<string>() { @"http://localhost/site1", @"http://localhost/site2", @"http://localhost/site3" };
foreach (string site in sites)
{
Begin(site); // fire off tasks
// the HandleControlMode method is async because in other circumstances, I do the following:
//await Task.Delay(5000); // sleep 5 seconds
}
// wait while all test running threads complete
try
{
Task.WaitAll(runningTasks.ToArray());
}
catch (Exception)
{
// not really a catch all handler...
}
}
catch (Exception)
{
// not really a catch all handler...
}
return result;
}
private static void Begin(string site)
{
//runningTasks.Add(Task.Factory.StartNew(() => HandlePlayback(runArgs)));
runningTasks.Add(Task.Factory.StartNew((args) => HandlePlayback((string)((object[])args)[0]), new object[] { site }));
}
private static async Task<int> HandlePlayback(string site)
{
int result = 1;
try
{
PlaybackEngine engine = new PlaybackEngine(site);
bool runResult = await engine.RunCommandLine(site);
if (!runResult)
{
result = 0;
}
}
catch (Exception)
{
result = 0;
}
return result;
}
}
public class PlaybackEngine
{
private static HttpClientHandler ClientHandler = new HttpClientHandler()
{
AllowAutoRedirect = false,
AutomaticDecompression = System.Net.DecompressionMethods.GZip | DecompressionMethods.Deflate
};
private static HttpClient Client = new HttpClient(ClientHandler);
public string Target { get; set; }
public PlaybackEngine(string target)
{
Target = target;
}
public async Task<bool> RunCommandLine(string site)
{
bool success = true;
string response = await this.SendRequest();
return success;
}
private async Task<string> SendRequest()
{
string result = string.Empty;
string requestTarget = Target;
HttpMethod method = HttpMethod.Post;
var message = new HttpRequestMessage(method, requestTarget);
StringContent requestContent = null;
requestContent = new StringContent("dummycontent", Encoding.UTF8, "application/x-www-form-urlencoded");
message.Content = requestContent;
try
{
using (HttpResponseMessage response = await Client.SendAsync(message))
{
using (HttpContent responseContent = response.Content)
{
result = await responseContent.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine(result);
}
}
}
catch (Exception ex)
{
}
return result;
}
}
}
UPDATE2: 我在http://rextester.com/CJS33330
上放了类似的代码它是一个直接的控制台应用程序,我已经添加了.ConfigureAwait(false)等待所有等待(没有效果)。在单独的测试中,我尝试了4或5种其他方法从Main调用第一个异步函数 - 这些函数都有效但行为相同。
答案 0 :(得分:0)
这段代码的问题在于,我不是在等待我认为的那些任务。 runningTasks集合接受任何类型的任务。我没有意识到Task.Factory.StartNew返回的类型不同于我试图启动的任务。我的函数返回
Task<int>
但StartNew返回
Task<Task<int>>
这些任务立即完成,因此主线程没有保持足够长的时间来运行实际例程。你必须等待内部任务:
Task<Task<int>> wrappedTask = Task.Factory.StartNew(...);
Task<int> t = await wrappedTask;
runningTasks.Add(t);
...
Task allTasks = Task.WhenAll(runningTasks.ToArray());
await allTasks;
出于某种原因,我无法使用内置的&#34; .Unwrap&#34;应该是等效的函数,但上面的代码完成了这项工作。