Загрузчик C #: я должен использовать Threads, BackgroundWorker или ThreadPool? - PullRequest
6 голосов
/ 02 августа 2011

Я пишу загрузчик на C # и остановился на следующей проблеме: какой метод я должен использовать для распараллеливания моих загрузок и обновления моего графического интерфейса?

В моей первой попытке я использовал 4 Threads ипо завершении каждого из них я запускал еще один: основная проблема заключалась в том, что мой процессор переходит на 100% при каждом запуске нового потока.

Погуглив, я обнаружил существование BackgroundWorker и ThreadPool: заявив, что я хочуобновлять мой графический интерфейс с прогрессом каждой ссылки, которую я загружаю, каково лучшее решение?

1) Создание 4 различных BackgroundWorker, присоединение к каждому событию ProgressChanged Делегата к функции в моем GUI для обновленияprogress?

2) Использовать ThreadPool и устанавливать максимальное и минимальное количество потоков в одно и то же значение?

Если я выберу # 2, когда в очереди больше нет потоков, останавливается ли она4 рабочие темы?Это приостанавливает их?Поскольку мне приходится загружать разные списки ссылок (по 20 ссылок каждая) и переходить от одной к другой, когда она завершена, запускает и останавливает ли ThreadPool потоки между каждым списком?

Если я хочу изменитьколичество рабочих потоков в прямом эфире и решили использовать ThreadPool, меняя с 10 потоков на 6, он выбрасывает и исключает и останавливает 4 случайных потока?

Это единственная часть, которая вызывает у меня головную боль.Я заранее благодарю каждого из вас за ваши ответы.

Ответы [ 4 ]

10 голосов
/ 02 августа 2011

Я бы предложил использовать 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.

4 голосов
/ 02 августа 2011

Я думаю, вам следует изучить возможность использования Task Parallel Library , которая является новой в .NET 4 и предназначена для решения подобных проблем

0 голосов
/ 02 августа 2011

Создавая 4 разных фоновых работника, вы будете создавать отдельные потоки, которые больше не будут мешать вашему графическому интерфейсу.Фоновые работники просты в реализации и, насколько я понимаю, будут делать именно то, что вам нужно.

Лично я бы сделал это и просто позволил бы другим не начинать, пока предыдущий не закончится.(Или, может быть, только один, и позволить ему выполнять по одному методу за раз в правильном порядке.)

FYI - Backgroundworker

0 голосов
/ 02 августа 2011

Загрузка процессора на 100% никак не связана с загрузкой (поскольку ваша сеть практически всегда является узким местом).Я бы сказал, что вы должны проверить свою логику того, как вы ожидаете завершения загрузки.

Можете ли вы опубликовать некоторый код кода потока, который вы запускаете несколько раз?

...