7

C# でダウンローダを作成していて、次の問題で停止しました: ダウンロードを並列化し、GUI を更新するには、どのような方法を使用すればよいですか?

私の最初の試みでは、4 つのスレッドを使用し、それぞれの完了時に別のスレッドを開始しました。主な問題は、新しいスレッドが開始されるたびに CPU が 100% になることでした。

グーグルで調べてみると、BackgroundWorker と ThreadPool の存在が見つかりました。ダウンロードしている各リンクの進行状況で GUI を更新したいのですが、最善の解決策は何ですか?

1) 4 つの異なる BackgroundWorker を作成し、各 ProgressChanged イベントに Delegate を GUI の関数にアタッチして進行状況を更新しますか?

2) ThreadPool を使用し、スレッドの最大数と最小数を同じ値に設定しますか?

#2 を選択した場合、キューにスレッドがなくなると、4 つの作業スレッドが停止しますか? それらを一時停止しますか?リンクのさまざまなリスト (それぞれ 20 個のリンク) をダウンロードし、1 つが完了すると別のリストに移動する必要があるため、ThreadPool は各リスト間でスレッドを開始および停止しますか?

ライブで作業中のスレッドの数を変更し、ThreadPool を使用して 10 スレッドから 6 スレッドに変更したい場合、スローして例外を発生させ、4 つのランダム スレッドを停止しますか?

頭を悩ませているのはこの部分だけです。ご回答いただきありがとうございます。

4

4 に答える 4

11

これに使用することをお勧めしWebClient.DownloadFileAsyncます。複数のダウンロードを行うことができ、それぞれDownloadProgressChangedが進行するにつれてイベントを発生させ、完了するとDownloadFileCompleted発生します。

セマフォでキューを使用するか、.NET 4.0 を使用している場合はBlockingCollection. 例えば:

// Information used in callbacks.
class DownloadArgs
{
    public readonly string Url;
    public readonly string Filename;
    public readonly WebClient Client;
    public DownloadArgs(string u, string f, WebClient c)
    {
        Url = u;
        Filename = f;
        Client = c;
    }
}

const int MaxClients = 4;

// create a queue that allows the max items
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>(MaxClients);

// queue of urls to be downloaded (unbounded)
Queue<string> UrlQueue = new Queue<string>();

// create four WebClient instances and put them into the queue
for (int i = 0; i < MaxClients; ++i)
{
    var cli = new WebClient();
    cli.DownloadProgressChanged += DownloadProgressChanged;
    cli.DownloadFileCompleted += DownloadFileCompleted;
    ClientQueue.Add(cli);
}

// Fill the UrlQueue here

// Now go until the UrlQueue is empty
while (UrlQueue.Count > 0)
{
    WebClient cli = ClientQueue.Take(); // blocks if there is no client available
    string url = UrlQueue.Dequeue();
    string fname = CreateOutputFilename(url);  // or however you get the output file name
    cli.DownloadFileAsync(new Uri(url), fname, 
        new DownloadArgs(url, fname, cli));
}


void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    DownloadArgs args = (DownloadArgs)e.UserState;
    // Do status updates for this download
}

void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    DownloadArgs args = (DownloadArgs)e.UserState;
    // do whatever UI updates

    // now put this client back into the queue
    ClientQueue.Add(args.Client);
}

スレッドを明示的に管理したり、TPL にアクセスしたりする必要はありません。

于 2011-08-02T15:04:19.400 に答える
4

.NET 4の新機能であり、この種の問題を解決するために設計されたタスク並列ライブラリの使用を検討する必要があると思います。

于 2011-08-02T14:16:33.057 に答える
0

100%のCPU負荷があることは、ダウンロードとは何の関係もありません(ネットワークは事実上常にボトルネックであるため)。ダウンロードが完了するのをどのように待つか、ロジックを確認する必要があります。

複数回開始したスレッドのコードのコードを投稿できますか?

于 2011-08-02T14:16:11.330 に答える
0

4 つの異なるバックグラウンド ワーカーを作成することで、GUI に干渉しない個別のスレッドを作成することになります。バックグラウンドワーカーは実装が簡単で、私が理解していることから、必要なことを正確に実行します。

個人的にはこれを行い、前のものが終了するまで他のものを開始しないようにします。(または、1 つだけにして、一度に 1 つのメソッドを正しい順序で実行できるようにすることもできます。)

参考までに-バックグラウンドワーカー

于 2011-08-02T14:24:06.320 に答える