2

Есть список задач. В данный момент я их запускаю без поток последовательно. Но это все проходит очень медленно и я задумался использовать потоки. Только вот не совсем понятно какую конструкцию лучше и проще здесь использовать? ThreadPool. Task и т.д.

var data1 = parse("www.ya.ru");
var data2 = parse("www.google.ru");
var data3 = parse("www.yahoo.ru");
....
Nick Volynkin
  • 34,094
Radzhab
  • 3,772
  • 1
    "какую конструкцию лучше и проще здесь использовать? ThreadPool. Task и т.д" -- Task. – Stack Jan 09 '16 at 15:29

3 Answers3

6

В дополнение к уже существующим ответам, возможно, в данной задаче вам стоит подумать насчёт Parallel LINQ:

var uris = new[] { "www.ya.ru", "www.google.ru", "www.yahoo.ru" };
var data = uris.AsParallel().Select(parse).ToList();

По поводу Task'ов, мне больше нравится такой синтаксис:

var uris = new[] { "www.ya.ru", "www.google.ru", "www.yahoo.ru" };
var tasks = uris.Select(uri => Task.Run(() => parse(uri));
var results = await Task.WhenAll(tasks);

Для того, чтобы не занимать свободные потоки без дела, я бы порекомендовал метод parse сделать асинхронным, тогда код становится эффективнее:

async Task<Data> parse(string uri)
{
    string content;
    using (var cl = new WebClient())
         content = await cl.DownloadStringTaskAsync(uri);
    // собственно разбор есть CPU-bound -- выгружаем в пул потоков
    return await Task.Run(() => ParseContent(content));
}

var uris = new[] { "www.ya.ru", "www.google.ru", "www.yahoo.ru" };
var tasks = uris.Select(parse);
var results = await Task.WhenAll(tasks);

Возможно, правильнее использовать более легковесный и современный HttpClient:

using (var cl = new HttpClient())
    content = await cl.GetStringAsync(uri);
VladD
  • 206,799
  • ".ToList();" -- представьте, что есть 200 тыс. страниц и с каждой получается по 500 KB. – Stack Jan 09 '16 at 16:39
  • 2
    @Stack: В вопросе написано «10 задач». Если количество задач реально больше, то дальнейшую их обработку, без сомнения, стоит включить в pipeline. – VladD Jan 09 '16 at 16:43
  • у меня на выходе List. Почему то на select ругается – Radzhab Jan 09 '16 at 16:55
  • @Radzhab: Какой у вас код? На что и как ругается? – VladD Jan 09 '16 at 17:01
  • @VladD "var results = await Task.WhenAll(tasks);" -- задачи, которые уже закончились, будут ждать завершение остальных. – Stack Jan 09 '16 at 21:40
  • @Stack: Угу. Если нужно обрабатывать каждую, то точно так же дальнейшая обработка должна быть по идее включена в задание вместе с загрузкой и разбором. Ожидать нужно лишь конечный результат. – VladD Jan 09 '16 at 22:10
  • @Radzhab если ругается на Select - значит, вы забыли про using System.Linq; – Pavel Mayorov Jan 12 '16 at 06:03
3

Проще использовать Task.

using System.Threading.Tasks; 

static void Main(string[] args) {

  var urls = new [] { 
               // адреса 
             };

  Task.Factory.StartNew(() => {
    foreach (var url in urls) 
       StartDownload(url);
  })
  .Wait();  // ждем завершение всех Task'ов, запущенных в StartDownload
} 

static void StartDownload(string url) { // для каждого url запускаем Task
  Task.Factory.StartNew(
    () => Download(url), 
    TaskCreationOptions.AttachedToParent
  );
}

static void Download(string url) {    // выполняется в отдельном потоке
  // скачиваем и парсим. 
}

Если из разных потоков надо выводить статистику в лог, то пример тут.
Если надо прервать работу в Task, то пример тут.

Stack
  • 9,452
1

Судя по коду который вы приводите в листинге лучше использовать Task.

Пример:

var data = Task<string>.Factory.StartNew(() => { parse("www.ya.ru"); } );

ThreadPool - же обычно используется если вам не нужно возвращать какие либо данные результатом.

AvtPhenix
  • 246
  • а как подождать пока он закончит процесс? – Radzhab Jan 09 '16 at 15:59
  • "а как подождать пока он закончит процесс?" -- см. .Wait() - в моем ответе. – Stack Jan 09 '16 at 16:02
  • 1
    Не надо Wait!!! Для этих целей давно уже конструкцию async/await придумали. – Pavel Mayorov Jan 12 '16 at 06:01