为何使用锁的两个线程未出现频繁线程ID切换?
线程锁与线程ID切换问题分析
示例代码
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security; using System.Threading; namespace Threads { class Program { static int i = 0; [DllImport("Kernel32.dll"), SuppressUnmanagedCodeSecurity] public static extern int GetCurrentProcessorNumber(); static void Main(string[] args) { object lockObject = new object(); var t1 = new Thread(CountToHundret); var t2 = new Thread(CountToHundret); t1.Start(lockObject); t2.Start(lockObject); Console.ReadKey(); } private static void CountToHundret(object lockObject) { while (i < 100) { lock (lockObject) { Console.WriteLine( ++i + "/" + Thread.CurrentThread.ManagedThreadId + "/" + GetCurrentProcessorNumber()); } } } } }
场景与疑问
这段代码通过两个线程共享迭代变量i计数到100,每次输出内容包含:当前计数值、线程ID、线程运行的处理器核心ID。
实际输出中线程ID切换次数极少,虽然核心ID显示线程在多个核心上执行,但并未出现预期中“线程ID交替输出”的情况。疑问在于:为何线程ID没有频繁切换,反而会出现连续多次由同一个线程输出的情况?
原因分析
调度器的效率优先策略
操作系统线程调度器的核心目标是降低执行开销、提升整体效率。当一个线程释放锁后,它的上下文(寄存器状态、缓存数据)还保留在当前核心的缓存中,调度器会优先让这个线程继续执行下一轮循环——直接再次获取锁的开销,远低于唤醒等待队列里的另一个线程、切换上下文的开销。锁的偏向性优化
.NET中的lock(本质是Monitor)存在偏向锁机制:如果一个锁被某个线程多次获取,调度器会倾向于让该线程继续持有锁,避免频繁的锁竞争和上下文切换。这种机制进一步强化了“同一个线程连续执行多轮”的情况。短任务的时间片特性
代码中线程每次持有锁时执行的逻辑非常简短(自增变量+控制台输出),远未耗尽线程的时间片。在时间片用完前,线程已经完成一轮循环并再次尝试获取锁,此时调度器没有理由切换到另一个线程。
只有当当前线程被阻塞(比如等待IO、时间片耗尽),或者系统负载极高时,调度器才会切换到等待队列中的另一个线程,这就是为什么只会看到偶尔的线程ID切换。
内容的提问来源于stack exchange,提问作者EmKay89
相关产品推荐
相关产品推荐

