以下是该方案:
如果此代理被阻止,则只有一个线程需要从ProxyQueue中取代代理,而不是所有代理。 为了出列我现在正在使用互锁,所以一次只有一个线程可以进入该功能。
private static volatile string httpProxy = "1.1.1.1";
private static int usingResource = 0;
string localHttpProxy;
try
{
HttpWebRequest oReqReview = HttpWebRequest)WebRequest.Create(url);
if (IsHttpProxyDequeue)
{
oReqReview.Proxy = new WebProxy(httpProxy, 8998);
localHttpProxy = httpProxy;
}
HttpWebResponse respReview = (HttpWebResponse)oReqReview.GetResponse();
DoSomthing();
}
catch (Exception ex)
{
if (0 == Interlocked.Exchange(ref usingResource, 1))
{
if (ex.Message == "The remote server returned an error: (403) Forbidden." && httpProxy == localHttpProxy)
{
IsHttpProxyDequeue = currentQueueProxy.TryDequeue(out httpProxy);
}
Interlocked.Exchange(ref usingResource, 0);
}
}
答案 0 :(得分:2)
Interlocked.Exchange不会阻止。它只是执行值的交换并报告结果。如果usingResource
的初始值为0且三个线程在同一时间点击Interlocked.Exchange
,则在一个线程上,Exchange()将返回零并将usingResource设置为1,并在另外两个线程上交换()将返回1.线程2和3将立即继续执行if块后的第一个语句。
如果您希望线程2和3阻止等待线程1完成,那么您应该使用类似于互斥锁的内容,例如C#lock(object)
语法。锁定阻止线程。
Interlocked.Exchange
不会阻止线程。编写非阻塞线程协调时,Interlocked.Exchange
非常有用。 Interlocked.Exchange
说“如果我从这次交换中获得特殊价值,我会绕道而行,进行这项特殊操作,否则我会继续做其他事情而不等待。”
答案 1 :(得分:1)
Interlocked
确实提供了该值的同步,因此如果多个线程同时到达该点,则只有其中一个线程会返回0
。所有其他人将获得1
,直到该值设置回'0'。
您的代码中存在竞争条件,这可能是造成问题的原因。考虑这一系列事件:
Thread A sees that `IsProxyDequeue` is `false`
Thread A calls `Interlocked.Exchange` and gets a value of '0'
Thread A logs the error
Thread B sees that `IsProxyDequeue` is `false`
Thread A dequeues the proxy and sets `usingResource` back to `0`
Thread B calls `Interlocked.Exchange` and gets a value of `0`
此时,线程B也将使代理出列。
您需要提供一种提供同步的不同方式。我怀疑你会想要这样的东西:
object lockObj = new object();
lock (lockObj)
{
if (!IsProxyDequeue)
{
// get the proxy
IsProxyDequeue = true;
}
oReqReview.Proxy = new WebProxy(httpProxy, 8989);
}
如果您想避免竞争条件,但又不想阻止其他线程,请使用Monitor.TryEnter
而不是lock
。