0

这是场景:

  1. 我有一个在所有线程之间共享的代理。
  2. 如果这个代理被阻塞,那么只有一个线程需要从 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);
    }
    }
    
4

2 回答 2

2

Interlocked.Exchange 不会阻塞。它仅执行值的交换并报告结果。如果 的初始值为usingResource0 并且三个线程Interlocked.Exchange同时命中,则在一个线程上 Exchange() 将返回零并将 usingResource 设置为 1,而在其他两个线程上 Exchange() 将返回 1。线程 2 和 3将立即继续执行 if 块之后的第一条语句。

如果您希望线程 2 和 3 阻止等待线程 1 完成,那么您应该使用诸如互斥锁之类的东西,例如 C#lock(object)语法。锁定阻塞线程。

Interlocked.Exchange不阻塞线程。 Interlocked.Exchange在编写非阻塞线程协调时很有用。Interlocked.Exchange说“如果我从这个交换中得到特殊的价值,我会绕道做这个特殊的操作,否则我会继续做这件事而无需等待。”

于 2013-03-19T16:20:03.037 回答
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.

于 2013-03-19T16:25:30.737 回答