You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用锁的两个线程未出现频繁线程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没有频繁切换,反而会出现连续多次由同一个线程输出的情况?

原因分析

  1. 调度器的效率优先策略
    操作系统线程调度器的核心目标是降低执行开销、提升整体效率。当一个线程释放锁后,它的上下文(寄存器状态、缓存数据)还保留在当前核心的缓存中,调度器会优先让这个线程继续执行下一轮循环——直接再次获取锁的开销,远低于唤醒等待队列里的另一个线程、切换上下文的开销。

  2. 锁的偏向性优化
    .NET中的lock(本质是Monitor)存在偏向锁机制:如果一个锁被某个线程多次获取,调度器会倾向于让该线程继续持有锁,避免频繁的锁竞争和上下文切换。这种机制进一步强化了“同一个线程连续执行多轮”的情况。

  3. 短任务的时间片特性
    代码中线程每次持有锁时执行的逻辑非常简短(自增变量+控制台输出),远未耗尽线程的时间片。在时间片用完前,线程已经完成一轮循环并再次尝试获取锁,此时调度器没有理由切换到另一个线程。

只有当当前线程被阻塞(比如等待IO、时间片耗尽),或者系统负载极高时,调度器才会切换到等待队列中的另一个线程,这就是为什么只会看到偶尔的线程ID切换。

内容的提问来源于stack exchange,提问作者EmKay89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 11:48:23