只在TPL中的一个线程中执行一次函数

时间:2013-03-19 15:41:37

标签: c# multithreading task-parallel-library

以下是该方案:

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

2 个答案:

答案 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