- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我需要递增一个计数器直到它达到一个特定的数字。我可以使用两个并行任务来增加数字。我没有使用锁来检查数字是否未达到最大允许值然后递增,而是使用 Interlocked .CompareExchange 以下列方式:
public class CompareExchangeStrategy
{
private int _counter = 0;
private int _max;
public CompareExchangeStrategy(int max)
{
_max = max;
}
public void Increment()
{
Task task1 = new Task(new Action(DoWork));
Task task2 = new Task(new Action(DoWork));
task1.Start();
task2.Start();
Task[] tasks = new Task[2] { task1, task2 };
Task.WaitAll(tasks);
}
private void DoWork()
{
while (true)
{
int initial = _counter;
if (initial >= _max)
{
break;
}
int computed = initial + 1;
Interlocked.CompareExchange(ref _counter, computed, initial);
}
}
}
这段代码比锁方法花费更多的时间来执行(对于 _max= 1,000,000):
public class LockStrategy
{
private int _counter = 0;
private int _max;
public LockStrategy(int max)
{
_max = max;
}
public void Increment()
{
Task task1 = new Task(new Action(DoWork));
Task task2 = new Task(new Action(DoWork));
task1.Start();
task2.Start();
Task[] tasks = new Task[2] { task1, task2 };
Task.WaitAll(tasks);
}
private void DoWork()
{
while (true)
{
lock (_lockObject)
{
if (_counter < _max)
{
_counter++;
}
else
{
break;
}
}
}
}
}
我使用 Interlocked.CompareExchange 的方式可能有问题,但我无法弄清楚。有没有更好的方法来执行上述逻辑而不使用锁(又名互锁方法)?
SpinWait spinwait = new SpinWait();
int lock =0;
while(true)
{
if (Interlocked.CompareExchange(ref lock, 1, 0) != 1)
{
if (_counter < _max)
{
_counter++;
Interlocked.Exchange(ref lock, 0);
}
else
{
Interlocked.Exchange(ref lock, 0);
break;
}
}
else
{
spinwait.SpinOnce();
}
}
区别在于旋转。如果任务无法在第一次执行时增加变量,它会自旋,为任务 2 提供进一步推进的机会,而不是执行繁忙的自旋等待。
我怀疑 lock 几乎做同样的事情,它可以采用自旋策略并允许当前拥有锁的线程执行。
最佳答案
这里的问题是您实际上在 Interlocked
版本中做了更多的工作——我的意思是更多的迭代。这是因为 很多 CompareExchange
没有做任何事情,因为值被改变了另一个线程。您可以通过向每个循环添加总计来看到这一点:
int total = 0;
while (true)
{
int initial = Thread.VolatileRead(ref _counter);
if (initial >= _max)
{
break;
}
int computed = initial + 1;
Interlocked.CompareExchange(ref _counter, computed, initial);
total++;
}
Console.WriteLine(total);
(请注意,我还添加了一个 VolatileRead
以确保 _counter
未保存在寄存器中)
我得到的迭代次数(通过 total
)远比您在这里可能期望的要多。关键是当以这种方式使用 Interlocked
时,您需要添加一个策略来处理值更改时发生的情况,即重试策略。
例如,粗略的重试策略可能是:
while (true)
{
int initial = Thread.VolatileRead(ref _counter);
if (initial >= _max)
{
break;
}
int computed = initial + 1;
if (Interlocked.CompareExchange(ref _counter, computed, initial)
!= initial) continue;
total++;
}
也就是说:不断重试,直到你让它工作 - 任何“执行”代码只会在检查之后发生(当前 total++
行所在的位置)。然而,这会使代码更加昂贵。
如果 lock
更便宜:使用 lock
。 lock
没有什么问题,而且确实在内部进行了非常优化。无锁并不自动等同于“最快”或“最简单”。
关于c# - 使用 Interlocked.CompareExchange 递增计数器直到一个值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15495459/
我希望以原子方式递增静态变量,同时以无锁方式将新值分配给实例字段。目标是让每个对象在创建时获得一个唯一的、递增的 ID,这样两个对象就不可能获得相同的 ID。 下面的代码能实现吗? class MyC
我在优化程序时遇到了一些奇怪的性能结果,这些结果显示在以下 BenchmarkDotNet 基准测试中: string _s, _y = "yo"; [Benchmark] public void E
很抱歉问了这么长的问题,但是有 Jon Skeet 的引用资料,所以对某些人来说可能是值得的。 简而言之: Interlocked.Read/Interlocked.Exchange 在 Mono 框
在我阅读 ReaderWriterLockSlim 时锁机构,There was this guy谁建议 Interlock函数可用于更精细的锁定 另外,我发现 here马克的另一个回答: ...Wr
假设我有一个变量“counter”,并且有几个线程使用Interlocked访问和设置“counter”的值,即: int value = Interlocked.Increment(ref coun
Interlocked.CompareExchange() 方法 ( docs ) 粗略地说: “我有一个变量,我想我知道它当前有什么值。如果我是对的,请将值更改为那个”。 关键是这个方法可以用来在多
例子: Thread a: Interlocked.Increment(ref x); Thread b: int currentValue = x; 假设线程 b 在线程 a 之后执行,线程 b
我们有一个方法可以维护我们应用程序中所有事件的全局序列索引。由于它是网站,因此预计具有线程安全的这种方法。线程安全的实现如下: private static long lastUsedIndex =
我见过很多不错的对象池实现。例如:C# Object Pooling Pattern implementation . 但似乎线程安全的总是使用锁,从不尝试使用 Interlocked.* 操作。 编
是 Interlocked.Increment(ref x)比 x++ 快或慢对于各种平台上的整数和多头? 最佳答案 它较慢,因为它强制操作以原子方式发生,并且充当内存屏障,消除了处理器围绕指令重新排
我有一个应用程序,它不断地(+-100 毫秒)从 PLC 读取订单,然后将它们放入模型中,然后由多个客户端读取。为此,我使用了 lock 语句。 订单阅读线程: lock (model) { //up
我们有一个并发的多线程程序。我如何使样本数每次增加 +5 间隔? Interlocked.Increment 是否有间隔过载?我没有看到它列出。 Microsoft Interlocked.Incre
作为一个线程菜鸟,我正在尝试找到一种不锁定对象的方法,允许我将线程池任务排入队列,这样它的最大并行度 = 1。 这段代码会按照我的想法行事吗? private int status; private
这仅涉及 Microsoft/Visual Studio 和 Intel/AMD 特定实现。 比如说,如果声明一个全局变量: volatile __declspec(align(16)) ULONG
我有几个简单的(希望是)问题,我一直无法找到答案 - 假设我有多个线程可以访问的对象 a、b。 Interlocked.Exchange(ref a, b) 如果“b”不是 volatile 的,这个
我正在编写一个需要使用 Interlocked 的通用类。 T test1, test2; Interlocked.Exchange(ref test1, test2); 这不会编译。所以我是否被迫使
我有一些遗留代码使用 Interlocked.Equals 来比较值。这些值可以是两个 bool 值,也可以将一个结构数组与 null 进行比较。 Resharper 提示 Interlocked.E
如果我要使用某种算法,在 C++ 中对变量使用 InterlockCompareExchange 操作来确定特定线程是否正在写入一组数据(通过创建我自己的小锁),我如何确保如果数据存储在 i7 上的
假设我们有这样一个类: public class Foo { private Bar bar = new Bar(); public void DoStuffInThread1()
来自文档 here :“此类的方法有助于防止在线程正在更新可由其他线程访问的变量时调度程序切换上下文时可能发生的错误...” 此外,对 this question 的回答声明“互锁方法在任何数量的内核
我是一名优秀的程序员,十分优秀!