WebClient.DownloadDataAsyncがUIをフリーズしています
質問
フォームコンストラクターには、InitializeComponentの後に次のコードがあります。
using (WebClient client = new WebClient())
{
client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
client.DownloadDataAsync("http://example.com/version.txt");
}
フォームを開始すると、client_DownloadDataCompletedが発生するまでUIは表示されません。 client_DownloadDataCompletedメソッドは空なので、問題はありません。
私が間違っていることは何ですか? UIをフリーズせずにこれを行うにはどうすればよいですか?
お時間をいただきありがとうございます。
よろしく。
完全なコード:
Program.cs
using System;
using System.Windows.Forms;
namespace Lala
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Form1.cs
using System;
using System.Net;
using System.Windows.Forms;
namespace Lala
{
public partial class Form1 : Form
{
WebClient client = new WebClient();
public Form1()
{
client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
client.DownloadDataAsync(new Uri("http://www.google.com"));
InitializeComponent();
}
void client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
textBox1.Text += "A";
}
}
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(12, 41);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(468, 213);
this.textBox1.TabIndex = 1;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(492, 266);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox1;
}
}
解決
完全なコードができたので、問題は絶対に見ていません-とにかく説明どおりではありません。
DownloadDataAsync呼び出しの直前と直後、およびいつ完了したハンドラーが起動されるかを示すログを少し取得しました。 3G経由で大きなファイルをダウンロードすると、「前」と「前」の間に一時停止があります および&quot; after&quot;ただし、ファイルのダウンロードが完了する前にUIが表示されます。
接続は同期的に行われると思われますが、実際のダウンロードは非同期です。もちろん、それはまだ残念です-そしておそらくすべてを別のスレッドにパントする方法があります-しかし、私が正しいなら、少なくとも知る価値があります。
他のヒント
同じ問題に遭遇し、解決策を見つけました。 ここでの非常に複雑な議論: http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl
要するに、問題はWebクライアントがプロキシサーバーを検索してアプリをハングさせていることです。 次の解決策が役立ちます:
WebClient webClient = new WebClient();
webClient.Proxy = null;
... Do whatever else ...
別のスレッドでダウンロードを実行する場合は、出発点として。
WebClientを非同期呼び出しに使用している間にWebClientを破棄することに関係があると強く疑います。
usingステートメントを削除して、代わりにイベントハンドラーでDisposeを呼び出してください。 (またはテストのためだけに、破棄することをまったく心配しないでください。
問題を示す短いが完全なプログラムを投稿できる場合、それは本当に便利でしょう。
他の人から言及されている非同期呼び出しをまだ実行している可能性のあるものを破棄するのと同様に、フォームのコンストラクターでこのような重いものを実行することを強くお勧めします。
代わりにOnLoadオーバーライドで実行します。VSModeデザイナでいくつかのレベルの地獄を回避するのに役立つDesignModeプロパティも確認できます。
削除されていない:多くの人が私のようにusingブロックについて考えているように、それが に関連していないことを確認しました。
usingブロックを削除できますか、webclientインスタンスを破棄するのを待っていると思います。
非UIスレッドでのDownloadDataAsyncとDownloadData:
DownloadDataAsyncは、要求が行われ、サーバーが応答した後、DownloadDataCompletedEventを処理するまで実際にスレッドを結び付けないため、便利です。
Jon Skeetは正しい軌道に乗っていると思います-非同期HTTP要求がキューに入れられ、DownloadDataAsync呼び出しが戻る前に、DNS解決が同期的に完了する必要があることを読みました。
DNSの解決は遅くなりますか?
VS2010、.NET 4のWPFプロジェクトで同じことをテストしました。
WebClient.DownloadDataCompletedなどを使用して完了した割合を示すプログレスバー付きのファイルをダウンロードしています
そして、驚いたことに、@ Danが言及したのと同じことがわかりました。 デバッガー内で、面白い方法でスレッドをブロックします。デバッグでは、進行状況メーターが1%で更新され、しばらくの間何も行わず、その後突然100%で更新されます。 (Debug.WriteLnステートメントは全体を通してスムーズに印刷されます)。そして、これら2つの時間の間、UIはフリーズします。
しかし、デバッガーの外部では、進行状況バーが0%から100%にスムーズに移動し、UIがフリーズすることはありません。それはあなたが期待するものです。
これを試してください:
client.Proxy = GlobalProxySelection.GetEmptyProxy();
それは私には少し奇妙に見えます。
WebClientのメンバーrefを保持して、コンストラクタで破棄しないようにしてください。おそらく、client.Dispose()でブロックしている
using()ステートメントは、ダウンロード中にWebClientのDispose()を呼び出そうとしています。 Disposeメソッドは、おそらくダウンロードが完了するのを待ってから続行します。
using()ステートメントを使用しないようにして、DownloadDataCompletedイベントでWebClientを破棄します。
コードを正常に実行できます。フォームが表示され、フォームが表示された後にダウンロードが完了します。
あなたが言ったようにフリーズはありません。
内部で実行している環境と関係があると思います。
.NET / Visual Studioのバージョンは何ですか?
私はあなたのコードを試してみましたが、うまく動作します。
Main(Args [])メソッドと、実行時にaおよびbの値を投稿できますか
int a, b;
ThreadPool.GetMaxThreads(out a, out b);
.NET 3.5およびVS2008で試しました。私は迷っていますが、それはあなたのマシンのセットアップに関係していると確信しています。コードではありません。以下を確認してください:
- スレッドプールを確認します(上記)。 a = 250 b = 1000を取得します
- すべてのサードパーティのプラグインを無効にします
- VSをロード&quot; Clean&quot ;; (再起動しました)
- できるだけ多くのプログラム/サービスを閉じます
- IEの設定を確認してください。クラスはIEコード/設定を使用していると思います
- ファイアウォール?アンチウイルス?
- 別のコンピューターで試してください
うーん...私はただ興味があります
ファイアウォールを使用していますか
マシンに何かファイアウォールがありますか?
たぶんZoneAlarm?
私の経験では、プロジェクトのデバッグ(Visual Studio内での実行)を実行するとき、およびサーバーに初めてアクセスするときに、スレッドを並べ替えてブロックします。
コンパイル済みのexeを実行すると、ブロックは認識されません。
この問題は、VS2015でもまだ進行中です。私は最終的にこれを理解しました。人々が使用しているコードに何も問題はありません。問題は実際にラベルコントロールにデータを書き込むことができる速度であり、これがプロセスをハングアップさせ、UIをフリーズさせます。参照するラベルを、progresschangedハンドラー内のテキストボックスに置き換えてみてください。これにより、UIのすべてのラグが解決されました。コードが他の人ではなく時々機能する理由を把握しようとして何時間も費やしたため、これが他の人の助けになることを願っています。