为什么这个功能使用100%CPU?

时间:2011-08-21 02:59:15

标签: c# foreach hang

private static string GetProxy()
{
    var rnd = new Random();
    if (Settings.Globals.UsedProxies.Count >= 100)
    {
        Settings.Globals.UsedProxies.Clear();
    }

Start:
    var inx = rnd.Next(0, Settings.Globals.Proxies.Count);
    var theProx = Settings.Globals.Proxies[inx];
    foreach (var item in Settings.Globals.UsedProxies)
    {
        if (item == theProx)
            goto Start;
    }
    Settings.Globals.UsedProxies.Add(theProx);
    return theProx;
}

我从5个线程的池中以10到30秒的随机间隔调用此代码。这使用100%的CPU并且系统非常糟糕。如果我注释掉我对GetProxy的调用,该应用程序仅使用7%的CPU。有什么想法吗?

这个想法是我有一个包含1000个代理的列表。一旦使用了proxie,我想将它添加到usedproxies列表中,并且永远不会使用已经使用过的代理。

4 个答案:

答案 0 :(得分:3)

您的有趣goto循环可以保证永久运行 您的代码从列表中选择一个随机项,循环直到找到该项,然后重新开始。

一旦使用了所有代理,您的代码将永远循环,因为它找不到要添加的代理。

此外,List<T>不是线程安全的,因此您的代码可能会以不可预测的方式失败。

答案 1 :(得分:1)

要回答实际问题,它使用了100%的CPU(在我假设的单核心机器上),因为一切都足够小以适应内存,我们只是循环并做一些检查。这非常占用CPU。

要创建未使用代理列表,您可以执行以下操作:

HashSet unused = new HashSet(Settings.Globals.Proxies);
List unused = all.ExceptWith(Settings.Globals.UsedProxies);
unused.ExceptWith(Settings.Globals.UsedProxies);

然后使用unused属性和unused.Countunused.GetEnumerator()集合中选择随机代理。

答案 2 :(得分:1)

严格来说,这不是对OP问题的回答(为什么这个函数占用100%的CPU),但OP有竞争条件的问题,这可能会使列表行为不规律。所以我认为我可以展示一种方法来处理

据我所知,代码从代理列表中分配随机代理字符串。代码检查这是否已经是免费的,如果不是它试图选择另一个代理字符串。

代码的一个问题是,声明此代码是同时调用的,但代码并不安全地同时访问。

处理它的一种方法是引入一个安全地处理并发访问的ProxyPool类。

下面是一些代码,可以作为如何构建ProxyPool类的起点:

namespace SO_ProxyPool
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;

    sealed class ProxyPool
    {
        readonly object m_lock = new object ();
        readonly Random m_random = new Random ();
        readonly HashSet<string> m_usedProxies = new HashSet<string>();
        readonly HashSet<string> m_freeProxies = new HashSet<string>();
        volatile int m_minSize;

        public ProxyPool (IEnumerable<string> availableProxies)
        {
            m_freeProxies = new HashSet<string> (availableProxies);
            m_minSize = m_freeProxies.Count;
        }

        /// <summary>
        /// Reserves a proxy, returns null if no proxy is available
        /// </summary>
        /// <returns>The reserver proxy or null if no proxy is available</returns>
        public string ReserveProxy ()
        {
            lock (m_lock)
            {
                if (m_freeProxies.Count == 0)
                {
                    return null;
                }

                var index = m_random.Next (0, m_freeProxies.Count);

                var proxy = m_freeProxies.ElementAt (index);

                var removeSuccessful = m_freeProxies.Remove (proxy);
                var addSuccessful = m_usedProxies.Add (proxy);
                Debug.Assert (removeSuccessful);
                Debug.Assert (addSuccessful);

                m_minSize = Math.Min (m_minSize, m_freeProxies.Count);

                return proxy;
            }
        }

        /// <summary>
        /// Returns the minimum size of the pool so far
        /// </summary>
        public int MinSize
        {
            get
            {
                return m_minSize;
            }
        }

        /// <summary>
        /// Frees a reserved proxy
        /// </summary>
        /// <param name="proxy">The proxy to free</param>
        public void FreeProxy (string proxy)
        {
            if (proxy == null)
            {
                return;
            }

            lock (m_lock)
            {
                var removeSuccessful = m_usedProxies.Remove (proxy);
                if (removeSuccessful)
                {
                    var addSuccessful = m_freeProxies.Add (proxy);
                    Debug.Assert (addSuccessful);
                }

            }
        }
    }

    class Program
    {
        static readonly ProxyPool s_proxyPool = new ProxyPool (
            new[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", }
            );

        static string GetProxy ()
        {
            return s_proxyPool.ReserveProxy ();
        }

        static void FreeProxy (string proxy)
        {
            s_proxyPool.FreeProxy (proxy);
        }

        static void SimplisticTestCase ()
        {
            var proxy = GetProxy ();
            // Do something relevant...
            if (proxy != null)
            {
                FreeProxy (proxy);
            }
        }

        static void Main (string[] args)
        {
            var then = DateTime.Now;

            const int count = 10000000;
            Parallel.For (0, count, idx => SimplisticTestCase ());

            var diff = DateTime.Now - then;

            Console.WriteLine (
                "#{0} executions took {1:0.00}secs, pool min size {2}", 
                count,
                diff.TotalSeconds,
                s_proxyPool.MinSize
                );
        }
    }
}

答案 3 :(得分:0)

试试这个(假设UsedProxies中的所有代理都可以在Proxies中找到):

List<string> unusedProxies = new List<string>(Settings.Globals.Proxies);
foreach (string proxy in Settings.Globals.UsedProxies)
{
    unusedProxies.Remove(proxy);
}

int inx = rnd.Next(0, unusedProxies.Count);
string proxy = unusedProxies[inx];
Settings.Globals.UsedProxies.Add(proxy);
return proxy;

这应该比您的版本更快,因为所有未使用的代理都在他们自己的单独列表中。然后,您可以使用rnd.Next获取随机代理,并确保该代理未被使用。