WebClient.DownloadDataAsync 并不是真正的异步
本文关键字:异步 DownloadDataAsync 并不是 WebClient | 更新日期: 2023-09-27 18:31:11
我需要从同一页面异步获取数据,并且不阻塞主线程。
我尝试使用类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 方法都将并行运行并在主线程代码运行期间完成(我使用 Thread.CurrentThread.Join 在代码中模拟了这一点)。但似乎两个 DownloadDataAsync 调用都没有在button1_Click结束之前完成(尽管有足够的时间)。
有没有办法改变这种行为,或者我应该使用另一种方法?
对于您正在观察的内容,有一个简单的(r)解释。 DownloadDataAsync
在后台使用System.ComponentModel.AsyncOperation
,它在DownloadDataAsync
操作开始时保持对SynchronizationContext
的引用,并在完成时回发到它。这可确保在具有有效SynchronizationContext
的应用程序(即 Windows 窗体,它似乎是您正在使用的)中启动下载的同一线程上引发DownloadDataCompleted
事件。
由于我们要回发到已被阻止的线程,因此完成处理程序必须等待它变得可用才能执行。
在button1_Click
处理程序开始时引入SynchronizationContext.SetSynchronizationContext(null);
,您将看到完成按照您最初的预期执行,然后按钮处理程序运行完成。
编辑
正如@PeterDuniho所指出的,SetSynchronizationContext(null)
不应在快速和肮脏的测试之外使用。证明围绕SynchronizationContext
的存在或不存在的理论的更好方法是使用单独的测试应用程序(即,如果已经在使用 Windows 窗体,请启动一个快速控制台项目以查看您的代码在没有SynchronizationContext
的情况下的行为)。弄乱SyncronizationContext.Current
的用途有限,会让你陷入痛苦的世界。
我认为你最好使用 WebClient
的 Task
API,async-await
它会使你的代码更整洁:
private async void button1_Click(object sender, EventArgs e)
{
await checkLink_async();
await checkLink_async();
await checkLink_async();
}
/// <summary>
/// Check the availability of IP server by starting async read from input sensors.
/// </summary>
/// <returns>Nothing</returns>
public async Task checkLink_async()
{
string siteipURL = "http://localhost/ip9212/getio.php";
// Send http query
var client = new System.Net.WebClient();
try
{
Task t = client.DownloadDataTaskAsync(siteipURL);
//tl.LogMessage("CheckLink_async", "http request was sent");
await t;
//tl.LogMessage("checkLink_DownloadCompleted", "http request was processed");
}
catch (System.Net.WebException e)
{
// tl.LogMessage("CheckLink_async", "error:" + e.Message);
}
}