Я прочитал много ответов по этому вопросу, но пока не нашел решение.Снова при использовании блокировки
У меня есть класс с атрибутом счетчика, имеющим проблему с кешированными значениями. Даже летучий не похоже на работу:
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' - нет. Он обновляется только в том случае, когда ОС приближается к фактическому началу этих потоков (что может быть после завершения цикла). –