Force Parallel.ForEach сделать один поток на элемент в коллекции - PullRequest
1 голос
/ 15 февраля 2012

Вот пример кода, который я использовал:

        Stopwatch s = new Stopwatch();
        s.Start();

        ParallelOptions po = new ParallelOptions();
        // hosts contain 23 items
        po.MaxDegreeOfParallelism = hosts.Count();

        Parallel.ForEach(hosts, po, p =>
            {
                using (TcpClient tcpClient = new TcpClient())
                {
                    IAsyncResult result = tcpClient.BeginConnect(p.Value, 80, null, null);
                    WaitHandle timeoutHandler = result.AsyncWaitHandle;
                    try
                    {
                        if (!result.AsyncWaitHandle.WaitOne(1000, false))
                        {
                            tasks.TryAdd(p.Key, new TaskCompleteResult { result = false, exc = new Exception("By timeout") });
                            tcpClient.Close();
                        }
                        else
                        {                             
                            tasks.TryAdd(p.Key, new TaskCompleteResult { result = true, exc = null });
                        }

                        tcpClient.EndConnect(result);
                    }
                    catch (Exception ex)
                    {
                        tasks.TryAdd(p.Key, new TaskCompleteResult { result = false, exc = ex });
                    }
                    finally
                    {
                        timeoutHandler.Close();
                    }
                }
            });

        s.Stop();
        Console.WriteLine(s.Elapsed.TotalSeconds);

Итак, как я думал, это даст мне 1 (или, может быть, 2, если мы обратим внимание на некоторые накладные работы) секунды, но это займет 4секунд.Parallel.ForEach берет потоки из некоторых подготовленных до пула потоков или создает новые?Как мне добиться 1-2 секунд работы?

1 Ответ

3 голосов
/ 15 февраля 2012

Потоки действительно полезны только в том случае, если вы используете CPU.Все, что вы делаете, - это когда множество потоков индивидуально ничего не делают и ничего не делают параллельно.

Я бы подошел к этому совершенно по-другому.Что вы делаете сейчас:

  • создайте много потоков
  • , каждый из которых создает дескриптор ожидания, а затем блокируется на срок до 1 секунды

Я бы перевернул это:

  • создать множество дескрипторов ожидания
  • , затем начать блокировку на то, что осталось от одна секунда

В самом простом (псевдокод):

WaitHandle[] handles = ... start all the async tasks...
Thread.Sleep(1000);
foreach(handle)
    handle.WaitOne(0) ... log result

Но возможно что-то, связанное с:

WaitHandle[] handles = ... start all the async tasks...
System.Threading.WaitHandle.WaitAll(handles, 1000);
foreach(handle ...)
    handle.WaitOne(0) ... log result

Также: вы можете подумать о замене new Exception("By timeout") на new TimeoutException().

...