如何防止webclient冻结程序

How to prevent webclient from freezing program

目前我正在使用网络客户端从站点下载几个 XML 文件。但是在下载期间,unity 会冻结,直到这些下载完成。这大约需要 30 秒,大部分时间被内部编译器调用的 waitforsecond() 占用,导致 12*2 1 秒等待调用。

有没有办法在另一个线程上绕过这个/ 运行 这个但仍然能够从统一中调用它?

 // the form is a login form
 public void PreObtainData(ref MonavisaRequestForm request, string dateAndTime, string fileDateAndTime)
    {
        if (!initialized)
            Initialize();
        try
        {
            if (!request.webclient.IsBusy && requestQueue.Count == 0)
            {
                request.url = request.url.Replace("&", "%26");
                request.url = request.url.Replace("+", "%2B");
                Uri uri = new Uri(string.Format("http://localhost/login.php?username={0}&password={1}&request={2}", request.username, request.password, request.url));
                request.webclient.DownloadFile(uri, @"Nioo Graph Data " + fileDateAndTime + ".xml");
            }
            else if (!request.webclient.IsBusy && requestQueue.Count > 0)
            {
                Uri uri = new Uri(string.Format("http://localhost/login.php?username={0}&password={1}&request={2}", requestQueue.Peek().username, requestQueue.Peek().password, requestQueue.Peek().url));
                requestQueue.Peek().webclient.DownloadFile(uri, @"Nioo Graph Data " + fileDateAndTime + ".xml");
                requestQueue.Dequeue();
            }
            else
            {
                requestQueue.Enqueue(request);
            }
        }
        catch (System.Net.WebException ex)
        {
            if (ex.Status != System.Net.WebExceptionStatus.ProtocolError)
            {
                throw;
            }
        }
    }

Microsoft 在 System.ComponentModel 命名空间中公开了一个 BackgroundWorker class,它正是为这些类型的操作而设计的。您所做的就是向您的表单添加一个后台工作者,并捕获与其关联的 DoWork 和 RunWorkerCompleted 事件。您将在 DoWork 事件中执行下载,然后在 RunWorkerCompleted 事件中执行任何完成工作 and/or 清理。

正如@Panagiotis 所指出的,我不确定您的问题与 Unity 的关系是否与 运行 防止主线程锁定的后台进程有关。试用 BackgroundWorker,看看它是否能实现您的目标。

而不是使用像 DownloadFile 这样的阻塞方法,你应该使用像 DownloadFileTaskAsync 这样的异步方法和 async/await 关键字来确保在方法 returns 数据之后继续处理:

 // the form is a login form
public async Task PreObtainData(ref MonavisaRequestForm request, string dateAndTime, string fileDateAndTime)
{
    if (!initialized)
        Initialize();
    try
    {
        if (!request.webclient.IsBusy && requestQueue.Count == 0)
        {

            ...
            await request.webclient.DownloadFileTaskAsync(uri, @"Nioo Graph Data " + fileDateAndTime + ".xml");
        }
        else if (!request.webclient.IsBusy && requestQueue.Count > 0)
        {
            ...
            await requestQueue.Peek().webclient.DownloadFileTaskAsync(uri, @"Nioo Graph Data " + fileDateAndTime + ".xml");
            requestQueue.Dequeue();
        }
        else
        {
            requestQueue.Enqueue(request);
        }
    }
    catch (System.Net.WebException ex)
    {
        if (ex.Status != System.Net.WebExceptionStatus.ProtocolError)
        {
            throw;
        }
    }
}

这也会将 PreObtainData 转换为异步方法。您还应该修改其调用者以处理任务。如果在事件处理程序中调用此方法,可以通过将处理程序的签名更改为 async void 而不是 void 来完成,例如:

private async voidForm1_Load(object sender, System.EventArgs e)
{
    ...
    await PreObtainData(...);
    ...
}