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

本文关键字:一个 执行 一次 线程 函数 TPL | 更新日期: 2023-09-27 18:04:37

场景如下:

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

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

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

如果你想让线程2和3阻塞等待线程1完成,那么你应该使用像互斥锁这样的东西,就像c# lock(object)语法一样。锁阻塞线程。

Interlocked.Exchange不阻塞线程。Interlocked.Exchange在编写非阻塞线程协调时很有用。Interlocked.Exchange说:"如果我从这个交换中得到了特殊的值,我就会绕道去做这个特殊的操作,否则我就会继续做其他的事情,而不会等待。"

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