神秘的负计数器值

本文关键字:计数器 | 更新日期: 2023-09-27 18:33:20

我正在为项目的每个sql调用创建新线程。有数百万个 sql 调用,所以我在新线程中调用一个过程来处理 sql 调用。

在这样做时,我想递增和递减计数器,以便我知道这些线程何时完成了 sql 查询。

令我惊讶的是,输出在计数器中显示负值。如何?当我从 0 开始并在过程开始时加 1 并在过程结束时减去 1 时?

这个 int 不会在程序的其他任何地方调用。. 以下是代码。

public static int counter=0;
while(!txtstream.EndOfStream)
{
    new Thread(delegate()
    {
        processline();
    }).Start();
    Console.WriteLine(counter);
}
public static void processline()
{
    counter++;
    sql.ExecuteNonQuery();
    counter--;
}

输出如下所示:

1
21
-2
-2
5

神秘的负计数器值

没什么神秘的,你正在使用线程,对吧?

++--运算符不是线程安全的。这样做。

public static void processline()
{
    Interlocked.Increment(ref counter);
    sql.ExecuteNonQuery();
    Interlocked.Decrement(ref counter);
}

如何克服

使用 Interlocked.IncrementInterlocked.Decrement 安全地更改计数器的值。

为什么会这样

您将计数器作为变量,该变量在多个线程之间共享。此变量是非易失性的,不受任何同步块包装,因此每个线程都有自己的该变量副本。因此,如果两个线程尝试同时更改其值,则值将被上次访问它的线程的副本覆盖。
假设您在两个不同的线程中启动代码:
  1. 最初计数器等于零,两个线程都复制了它
  2. 两个线程调用都会递增其缓存的副本,并比更改计数器。因此,thread1 将其副本递增到 1 并覆盖计数器,thread2 还将其副本(仍等于零(递增为 1 并再次覆盖计数器 1。之后,该值将传播到所有线程(刷新所有副本(
  3. 两个线程都调用 sql 查询。由于 sql 性能的可变性,这些查询在不同的时间内完成。
  4. 线程 1 结束 sql 查询,将计数器从 1 递减为 0。计数器值传播到所有线程
  5. 一段时间后,Thread2 结束 sql 查询,将计数器从已经传播的 0 递减到 -1。计数器值将传播到所有线程。它是-1。