多Task下参数降至阈值1后仍被修改,如何修复?
问题根源
- 线程竞争:多个Task同时读取
param,判断param>1为真后,都会执行减0.1操作。哪怕param已经是1.1,多线程同时操作会直接把它降到0.9以下。 - 无效延迟:原代码中
Task.Delay(100)未使用await,延迟根本不生效,线程会立即执行减操作,进一步加剧竞争。 - 非原子操作:判断
param>1和执行param -=0.1是两个独立步骤,中间无同步,多个线程可插入执行,导致错误。
修复方案
核心思路:用锁保证对param的原子操作,修正异步逻辑,优化取消机制
修复后代码
class Program { static decimal param = 100; static readonly object _lock = new object(); static CancellationTokenSource _cts = new CancellationTokenSource(); static CancellationToken _token; static async Task Main(string[] args) { int taskCount = 16; _token = _cts.Token; Console.WriteLine("Start Param = {0}", param); Console.WriteLine("Tasks Count = {0}", taskCount); var tasks = Enumerable.Range(0, taskCount) .Select(_ => Task.Run(DecrementAsync, _token)) .ToList(); await Task.WhenAll(tasks); Console.WriteLine("Result = {0}", param); Console.Read(); } private static async Task DecrementAsync() { while (!_token.IsCancellationRequested) { bool canDecrement = false; // 锁内判断是否允许减操作 lock (_lock) { if (param > 1) { canDecrement = true; } else { _cts.Cancel(); return; } } if (canDecrement) { await Task.Delay(100, _token); // 锁内执行减操作,再次检查避免延迟期间参数已达阈值 lock (_lock) { if (param > 1) { param -= 0.1m; // 减完立即检查,触发取消 if (param <= 1) { _cts.Cancel(); } } } } } } }
关键修复点
- 锁机制:所有对
param的读写操作都在lock块内完成,确保同一时刻只有一个线程能操作该变量,彻底消除竞争条件。 - 异步修正:将
Decrementation改为async Task方法,用await Task.Delay(100, _token)实现正确的100ms延迟,同时支持取消延迟。 - 双重检查:延迟前后分别判断
param状态,避免延迟期间其他线程已将参数降到阈值以下,导致无效减操作。 - 取消优化:参数达到阈值时立即触发取消令牌,让所有Task尽快终止,避免不必要的循环。
效果验证
修复后无论多少个Task并发执行,param最终都会精确等于1,不会出现低于1的情况。
内容的提问来源于stack exchange,提问作者Сергей
相关产品推荐
相关产品推荐

