当前位置: 首页 > 知识库问答 >
问题:

递归调用函数使用的线程安全计数器

贺光华
2023-03-14

当使用名为GlobalID的全局整数作为计数器时,以下代码可以正常工作:

MyFunction(myClass thisClass, Int ID)
{   
     ListOfMyClass.add(thisClass)

     If (thisClass.IsPropertyValueAboveZero)
     {
          ID = GlobalID;
     }

     GlobalID++;

     Foreach (class someClass in ListOfClasses)
     {
           MyClass newClass = new MyClass(GlobalID, ID)
           MyFunction(newClass, ID)  //Recursive Call
     }
 }

我基本上是使用GlobalID作为每次调用函数时的递增计数器。计数器第一次执行时会被分配一个起始位置。我之所以使用全局变量,是因为我想确保每次传递的ID都准确增加,而不管执行是否进入递归调用。调用此函数(第一次…)来自分配全局变量起始位置的ForEach循环

我的目标是在初始调用中使用一个并行的。Foreach而不是常规的For per循环。我的问题是处理计数器。我不知道如何在多个线程中管理该计数器。如果我将其作为变量传递给函数,我相信我会有一个不准确的较低/已用数字离开递归循环。全局变量确保下一个数字高于上一个数字。thisClass. IsProperty tyValueAboveZero只是一种基于条件语句描述操作的任意方式。它对其余代码没有有意义的引用。

如果我有多个线程的计数器具有不同的起始位置,我如何确保此线程安全?目前我看到的唯一方法是手动编写相同函数和计数器的多个版本并使用TaskFactory

共有3个答案

慕晨
2023-03-14

对于单个计数器的线程安全整数递增,请考虑使用Interlocked。增量

将变量存储在静态文件中:

public static int Bob;

然后在静态函数中递增它:

public static int IncrementBob()
{
    return Interlocked.Increment(ref Bob);
}

任何时候你想增加,调用增量Bob

宿镜
2023-03-14

如果将变量用作联锁的反标准用法。增量有效(参见C#线程安全快速(est)计数器)。由于您的代码实际上想要使用计数器的值,因此必须非常小心地获取正确的值,因为必须同时保护从该值读取的数据。

因为你只需要在增加值时读取值,而不是联锁。增量仍然足够。当多个线程可以修改时,请确保不要直接检查或使用value或GlobalID

var ID = someValue; 
var newValue = Interlocked.Increment(ref GlobalID);
if (thisClass.IsPropertyValueAboveZero)
{
      // you can't use GlobalID directly here because it could be already incremented more
      ID = newValue - 1; // note -1 to match your original code.

}

否则,代码会变得更加复杂——例如,如果您只是偶尔想增加全局值,但每次调用都要使用它(这可能不是您的情况,因为示例显示递增总是使用较少的次数),那么您必须将当前值单独传递给函数。在这种情况下,传递给函数和实际计数器的值将互不相关。如果用例不是“仅在递增时使用价值”,那么您可能应该回顾您试图实现的目标——很可能您需要其他一些数据结构。请注意,使用lock仅保护读写计数器并不能解决线程安全问题,因为您无法预测要读取的值(可能会被其他线程增加多次)。

注意:文章中的示例代码显示了一些旧ID值和最新计数器值的不清楚用法。如果代码实际上反映了您想要做的事情,那么将“计算调用数”与“对象ID”分开将非常有用。

濮金鑫
2023-03-14

对于线程安全计数,请使用联锁方法:System。穿线。互锁的。增量(参考globalID)

private static int globalID;

// Be very careful about using a property to expose the counter
// do not *use* this value directly, but can be useful for debugging
// instead use the return values from Increment, Decrement, etc.
public int UnsafeGlobalID { get { return globalID; } }

注意:您需要为ref使用一个字段。可以通过属性公开字段,但在代码中可能会出现问题。最好使用联锁方法,如联锁。围绕需要同步值的逻辑,比较exchange或explicitlock语句。递增递减等的返回值通常是逻辑内部应该使用的值。

我的问题与柜台有关。我不知道如何在多个线程中管理该计数器。如果我将其作为变量传递给函数,我相信我将有一个不准确的下限/已用数离开递归循环。全局变量确保下一个数字高于上一个数字。

如果我有多个线程的计数器具有不同的起始位置,我如何确保此线程安全?目前我看到的唯一方法是手动编写相同函数和计数器的多个版本并使用TaskFactory

MyFunction(newClass,ID)的递归调用将具有ID的值,但我不确定如果(thisClass.isPropertyValue高于零)应该做什么。是为了确保你有一个非零的起点吗?如果是这样的话,最好在这个函数之外的初始调用之前确保它不是零。

而且foreach循环中的逻辑对我来说没有意义。在MyClass newClass=newmyclass(GlobalID,ID)中,ID将是参数值,或者如果isPropertyValue高于零为真,则将是GlobalID的当前值。因此ID通常小于GlobalID,因为GlobalIDforeach循环之前递增。我认为你在不需要的时候通过了GlobalID。

// if IsPropertyValueAboveZero is intended to start above zero
// then you can just initialize the counter to 1
private static int globalID = 1;

public void MyFunction(myClass thisClass, int id)
{
    // ListOfMyClass and ListOfClasses are probably not thread-safe
    // and you may need to add locks around the Add and get a copy
    // of ListOfClasses before the foreach enumeration
    // You may want to look at html" target="_blank">https://stackoverflow.com/a/6601832/29762
    ListOfMyClass.Add(thisClass); 

    foreach (class someClass in ListOfClasses)
    {
        int newId = System.Threading.Interlocked.Increment(ref globalID);
        MyClass newClass = new MyClass(newId);
        MyFunction(newClass, newId);  //Recursive Call
    }
}
 类似资料:
  • 问题内容: 我可以在变量中创建一个递归函数,如下所示: 这样,将输出 。假设我做了以下事情: 将输出 如上。如果我再更改如下: 然后将给出,如预期的那样。 现在给出 它所指的,而不是函数(它本身指向的)。在某些情况下这可能是理想的,但是有没有一种方法可以编写函数以便它调用自身而不是保存它的变量? 也就是说,是否可以 仅 更改线路,以便 在调用时仍能完成所有这些步骤?我试过了,但这给了我错误。 问题

  • 问题内容: 我有一个异步函数,要连续多次调用。问题是“多个”可以是几十万或数百万… 显而易见的方法是从回调中调用相同的函数,如下所示: 当然,涉及一些逻辑来停止递归。问题是堆栈是否充满了调用,并可能在某些时候导致堆栈溢出? 问题答案: 问题是堆栈是否充满了调用,并可能在某些时候导致堆栈溢出? 否。 如果调用回调是异步传递的,则不会堆积堆栈。 在您的代码中: 这是逐步发生的事情: 首先被称为。 然后

  • 第二个构造函数应该调用第一个构造函数,但却给了我“递归构造函数调用”错误。 我明白这个错误的意思,只是不明白递归在哪里。第一个contructor将作为参数,而应该是该类型的数组。我错过了什么? 多谢了。

  • 问题内容: 我必须用Java编写幂方法。它接收两个整数,无论​​它们是正数还是负数都没有关系。它应该具有的复杂度。它还必须使用递归。我当前的代码有两个数字,但我不断输出的结果是零,我不知道为什么。 问题答案: 让我们从一些数学事实开始: 对于正n,aⁿ=a⨯a⨯…⨯an次 对于负数n,aⁿ=⅟a⁻ⁿ=⅟(a⨯a⨯…⨯a)。这意味着 a 不能为零。 对于n = 0,即使 a 为零或负,aⁿ= 1 。

  • 需求:生成一个集合的所有可能组合的算法,没有重复项,或者递归调用函数返回结果。 在JavaScript中排列提供的大部分答案,如果不是全部的话?从循环或其他函数中递归调用函数以返回结果。 循环内递归函数调用示例 在为一个集合确定了各个置换的总数之后,可以使用、和创建并填充包含所有六个置换的结果数组 在计算排列和工作面试问题时,试图根据图中显示的模式再现有序词典排列算法的结果,该算法是基于C++实用

  • 虽然我的C程序显然不是线程安全的,但我需要从Java的多线程并行调用它。由于性能问题,我无法重写C代码,需要执行通过并行编译C程序创建的DLL。 如何从Java的多线程调用非线程安全的C函数? 我尝试的是使用JNA并在每次线程调用DLL函数时调用。但是,它并没有解决这个问题,并且出现了分割错误。 我认为DLL的新内存空间是通过调用分配的,应该避免重入问题。但是,我的想法可能是错误的。 我搜索了从J