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列表中,并且永远不会使用已经使用过的代理。
答案 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.Count
从unused.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
获取随机代理,并确保该代理未被使用。