C# Downloader: should I use Threads, BackgroundWorker or ThreadPool?

后端 未结 4 1183
遥遥无期
遥遥无期 2020-12-05 12:34

I\'m writing a downloader in C# and stopped at the following problem: what kind of method should I use to parallelize my downloads and update my GUI?

In my first att

4条回答
  •  清歌不尽
    2020-12-05 13:09

    I would suggest using WebClient.DownloadFileAsync for this. You can have multiple downloads going, each raising the DownloadProgressChanged event as it goes along, and DownloadFileCompleted when done.

    You can control the concurrency by using a queue with a semaphore or, if you're using .NET 4.0, a BlockingCollection. For example:

    // 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 ClientQueue = new BlockingCollection(MaxClients);
    
    // queue of urls to be downloaded (unbounded)
    Queue UrlQueue = new Queue();
    
    // 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);
    }
    

    There's no need for explicitly managing threads or going to the TPL.

提交回复
热议问题