我需要异步从同一页面获取数据,不要阻止主线程 .
我尝试使用类WebClient的DownloadDataAsync方法,但似乎它的行为不是真正的异步方式 .
为了测试这个,我制作代码
private void button1_Click(object sender, EventArgs e)
{
checkLink_async();
Thread.CurrentThread.Join(5000);
checkLink_async();
Thread.CurrentThread.Join(5000);
checkLink_async();
Thread.CurrentThread.Join(5000);
}
/// <summary>
/// Check the availability of IP server by starting async read from input sensors.
/// </summary>
/// <returns>Nothing</returns>
public void checkLink_async()
{
string siteipURL = "http://localhost/ip9212/getio.php";
Uri uri_siteipURL = new Uri(siteipURL);
// Send http query
WebClient client = new WebClient();
try
{
client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(checkLink_DownloadCompleted_test);
client.DownloadDataAsync(uri_siteipURL);
tl.LogMessage("CheckLink_async", "http request was sent");
}
catch (WebException e)
{
tl.LogMessage("CheckLink_async", "error:" + e.Message);
}
}
private void checkLink_DownloadCompleted_test(Object sender, DownloadDataCompletedEventArgs e)
{
tl.LogMessage("checkLink_DownloadCompleted", "http request was processed");
}
日志的结果:
01:32:12.089 CheckLink_async http request was sent
01:32:17.087 CheckLink_async http request was sent
01:32:22.097 CheckLink_async http request was sent
01:32:27.102 checkLink_DownloadComplet http request was processed
01:32:27.102 checkLink_DownloadComplet http request was processed
01:32:27.102 checkLink_DownloadComplet http request was processed
我期望每个启动的DownloadDataAsync方法将并行运行并在主线程代码runnig期间完成(我使用Thread.CurrentThread.Join在代码中模拟它) . 但似乎在button1_Click结束之前,没有任何DownloadDataAsync调用都已完成(尽管有足够的时间) .
有没有办法改变这种行为,或者我应该使用另一种方法?
2 回答
我认为你最好使用
WebClient
的Task
与async-await
它使你的代码更整洁:对于您正在观察的内容,有一个简单的(r)解释 .
DownloadDataAsync
在封面下使用System.ComponentModel.AsyncOperation
,它在DownloadDataAsync
操作开始时保持对SynchronizationContext
当前的当前引用,并在完成时发回给它 . 这样可以确保在具有有效SynchronizationContext
的应用程序(即Windows窗体,它看起来就是您正在使用的窗体)中启动下载的同一线程上引发DownloadDataCompleted
事件 .由于我们将回发到已经被阻止的线程,因此完成处理程序必须等待它才能执行 .
在
button1_Click
处理程序的开头引入SynchronizationContext.SetSynchronizationContext(null);
,在按钮处理程序运行完成之前,您将看到完成按照原始期望执行 .EDIT
正如@PeterDuniho所指出的那样,
SetSynchronizationContext(null)
不应该在快速和肮脏的测试之外使用 . 一种更好的方法来证明围绕存在或缺少SynchronizationContext
的理论是使用单独的测试应用程序(即,如果已经使用Windows窗体,启动一个快速的控制台项目,以查看代码在没有SynchronizationContext
的情况下的行为) . 与SyncronizationContext.Current
混淆的用途有限,可以让你陷入痛苦的世界 .