Skip to main content
 首页 » 编程设计

c#之再次使用互锁

2024年02月05日30over140

关于这一点,我看了很多答案,但我还没有找到解决办法。

我有一个带有计数器属性的类,它的缓存值有问题。甚至 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 不会立即启动任务。它将任务添加到线程池队列并返回。

在您的例子中,整个 for 循环在新任务开始运行之前执行,因此 _threadsCounter 没有任何变化。这就是为什么 volatile 没有帮助。