再次使用互锁
Again on using interlocked
关于这一点我看了很多答案,但我还没有找到解决办法。
我有一个 class,其计数器属性的缓存值有问题。甚至 volatile 似乎也不起作用:
public class MyClass {
private Timer _timer;
private int _threadsCounter = 0;
public StreamWriter Tracer { get; set; }
public MyClass() {
_timer = new Timer(1000.0 * 10);
_timer.AutoReset = true;
_timer.Elapsed += new ElapsedEventHandler(OnTimer);
_timer.Start();
}
private void OnTimer(object sender, ElapsedEventArgs e) {
HashSet<Task> taskPool = new HashSet<Task>();
try {
if (Tracer != null) Tracer.WriteLine("[{0}] onTimer start. Current threads counter is {1}.", DateTime.Now, _threadsCounter);
if (_threadsCounter >= 10) return;
// create parallel tasks
for (int i = 0; i < 8; i++) {
// limit on the max num of parallel processing but the counter remains unchanged during this timer event!!!
if (_threadsCounter >= 10) break;
var timeout = (30 + i * 2);
var task = Task.Run(() => {
var localCounter = System.Threading.Interlocked.Increment(ref _threadsCounter);
try {
System.Threading.Thread.Sleep(timeout * 1000);
}
finally {
System.Threading.Interlocked.Decrement(ref _threadsCounter);
}
});
taskPool.Add(task);
}
}
finally {
if (Tracer != null)
Tracer.WriteLine("[{0}] onTimer end. Created {1} tasks. Current threads counter is {2}.", DateTime.Now, taskPool.Count, _threadsCounter);
}
}
好吧,似乎 onTimer 缓存了 _threadsCounter 变量,因为输出是:
[14:10:47] onTimer start. Current threads counter is 0.
[14:10:47] onTimer end. Created 8 tasks. Current threads counter is 0.
[14:10:57] onTimer start. Current threads counter is 8.
[14:10:57] onTimer end. Created 8 tasks. Current threads counter is 8.
[14:11:07] onTimer start. Current threads counter is 16.
[14:11:07] onTimer end. Created 0 tasks. Current threads counter is 16.
[14:11:17] onTimer start. Current threads counter is 15.
[14:11:17] onTimer end. Created 0 tasks. Current threads counter is 15.
[14:11:37] onTimer start. Current threads counter is 4.
[14:11:37] onTimer end. Created 8 tasks. Current threads counter is 4.
[14:11:47] onTimer start. Current threads counter is 8.
[14:11:47] onTimer end. Created 8 tasks. Current threads counter is 8.
[14:11:57] onTimer start. Current threads counter is 16.
[14:11:57] onTimer end. Created 0 tasks. Current threads counter is 16.
为什么我到了 16 岁?
我通过稍微更改代码解决了这个问题:
var localCounter = _threadsCounter;
...
if ((localCounter + taskPool.Count) >= 10) break;
但为什么会出现这种行为?
Task.Run
不会立即开始任务。它将任务添加到线程池队列和 returns.
在您的例子中,整个 for
循环在新任务开始 运行 之前执行,因此没有任何变化 _threadsCounter
。这就是 volatile
没有帮助的原因。
您正在有效地测试实际开始的任务数,并达到递增计数器的程度。这需要一点时间 - 所以基本上你正在创建所有 8 个任务并启动它们, 然后 它们正在增加计数器......到那时你才注意到你已经太晚了'我已经超过 10 个了。
更好的解决方案是在开始任务之前递增计数器:
// Increment the counter in expectation of starting a task
var localCounter = Interlocked.Increment(ref _threadsCounter);
if (localCounter >= 10)
{
// Ah, we're not going to start a task after all, so undo
// the increment
Interlocked.Decrement(ref _threadsCounter);
break;
}
else
{
// Start a task, which will decrement the counter at the end.
// (You could add the "decrement" bit as a continuation, even...)
}
关于这一点我看了很多答案,但我还没有找到解决办法。
我有一个 class,其计数器属性的缓存值有问题。甚至 volatile 似乎也不起作用:
public class MyClass {
private Timer _timer;
private int _threadsCounter = 0;
public StreamWriter Tracer { get; set; }
public MyClass() {
_timer = new Timer(1000.0 * 10);
_timer.AutoReset = true;
_timer.Elapsed += new ElapsedEventHandler(OnTimer);
_timer.Start();
}
private void OnTimer(object sender, ElapsedEventArgs e) {
HashSet<Task> taskPool = new HashSet<Task>();
try {
if (Tracer != null) Tracer.WriteLine("[{0}] onTimer start. Current threads counter is {1}.", DateTime.Now, _threadsCounter);
if (_threadsCounter >= 10) return;
// create parallel tasks
for (int i = 0; i < 8; i++) {
// limit on the max num of parallel processing but the counter remains unchanged during this timer event!!!
if (_threadsCounter >= 10) break;
var timeout = (30 + i * 2);
var task = Task.Run(() => {
var localCounter = System.Threading.Interlocked.Increment(ref _threadsCounter);
try {
System.Threading.Thread.Sleep(timeout * 1000);
}
finally {
System.Threading.Interlocked.Decrement(ref _threadsCounter);
}
});
taskPool.Add(task);
}
}
finally {
if (Tracer != null)
Tracer.WriteLine("[{0}] onTimer end. Created {1} tasks. Current threads counter is {2}.", DateTime.Now, taskPool.Count, _threadsCounter);
}
}
好吧,似乎 onTimer 缓存了 _threadsCounter 变量,因为输出是:
[14:10:47] onTimer start. Current threads counter is 0.
[14:10:47] onTimer end. Created 8 tasks. Current threads counter is 0.
[14:10:57] onTimer start. Current threads counter is 8.
[14:10:57] onTimer end. Created 8 tasks. Current threads counter is 8.
[14:11:07] onTimer start. Current threads counter is 16.
[14:11:07] onTimer end. Created 0 tasks. Current threads counter is 16.
[14:11:17] onTimer start. Current threads counter is 15.
[14:11:17] onTimer end. Created 0 tasks. Current threads counter is 15.
[14:11:37] onTimer start. Current threads counter is 4.
[14:11:37] onTimer end. Created 8 tasks. Current threads counter is 4.
[14:11:47] onTimer start. Current threads counter is 8.
[14:11:47] onTimer end. Created 8 tasks. Current threads counter is 8.
[14:11:57] onTimer start. Current threads counter is 16.
[14:11:57] onTimer end. Created 0 tasks. Current threads counter is 16.
为什么我到了 16 岁? 我通过稍微更改代码解决了这个问题:
var localCounter = _threadsCounter;
...
if ((localCounter + taskPool.Count) >= 10) break;
但为什么会出现这种行为?
Task.Run
不会立即开始任务。它将任务添加到线程池队列和 returns.
在您的例子中,整个 for
循环在新任务开始 运行 之前执行,因此没有任何变化 _threadsCounter
。这就是 volatile
没有帮助的原因。
您正在有效地测试实际开始的任务数,并达到递增计数器的程度。这需要一点时间 - 所以基本上你正在创建所有 8 个任务并启动它们, 然后 它们正在增加计数器......到那时你才注意到你已经太晚了'我已经超过 10 个了。
更好的解决方案是在开始任务之前递增计数器:
// Increment the counter in expectation of starting a task
var localCounter = Interlocked.Increment(ref _threadsCounter);
if (localCounter >= 10)
{
// Ah, we're not going to start a task after all, so undo
// the increment
Interlocked.Decrement(ref _threadsCounter);
break;
}
else
{
// Start a task, which will decrement the counter at the end.
// (You could add the "decrement" bit as a continuation, even...)
}