简体   繁体   中英

Force Parallel.ForEach make one thread per item in collection

Here is code sample I have used:

        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);

So, as I thought it will give me 1 (or maybe 2, if we take in attention some overhead work) seconds, but it take 4 seconds. Does Parallel.ForEach takes threads from some prepared before thread pool or creates the new ones? How can I achieve 1-2 seconds work?

Threads are only really useful if your constraint is CPU. All you are doing is having lots of threads individually doing nothing, and doing nothing in parallel.

I would approach this a very different way. What you are doing now is:

  • create lots of threads
  • each of which creates a wait handle and then blocks for up to 1 second

I would turn it around:

  • create lots of wait handles
  • then start blocking for what remains of one second

At the simplest (pseudo-code):

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

But possibly something involving:

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

Also: you might consider replacing new Exception("By timeout") with new TimeoutException() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM