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