【发布时间】:2016-02-06 08:56:43
【问题描述】:
我阅读了很多关于这一点的答案,但我还没有找到解决方案。
我有一个带有计数器属性的类,它的缓存值有问题。甚至 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;
但是为什么会有这种行为呢?
【问题讨论】:
-
看起来您正在循环中检查
_threadsCounter,但更改_threadsCounter的实际代码在您的任务中,该任务正在不同的线程上运行。因此,您的_threadsCounter当然不会在下次循环时增加。您在上一次迭代中开始的任务可能还没有开始。 -
也许你已经幸运地得到了“修复”。确实,如果
_threadsCounter是多个线程之间的共享资源,您需要同步访问此共享资源的所有读/写。 -
@ChrisO:不走运,
taskPool.Count实际上是在将任务添加到池中时更新的。换句话说,在循环中。而_threadsCounter不是。只有当操作系统开始实际启动这些线程时(可能是在循环完成之后),它才会更新。 -
@MattBurland 你是对的!我不认为任务仍然悬而未决。谢谢
-
@MattBurland 知道了,谢谢。仍然需要在
_threadsCounter上进行同步访问,这在两个地方缺失。
标签: c# multithreading system.timers.timer