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

调用SynchronizationContext.Post时SendOrPostCallback未在同线程执行的问题

问题描述

我准备了一个C#示例,多次运行会在日志中看到异常,在Visual Studio 2022或VS Code中编译运行可查看详细错误信息:

System.InvalidOperationException
HResult=0x80131509
Message=Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
Source=System.Private.CoreLib
StackTrace:
at System.ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported()
at System.Collections.Generic.Dictionary2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary2.set_Item(TKey key, TValue value)
at SyncContextTest.PlayloadAccumulationHandler.AddCallback(Object state) in PlayloadAccumulationHandler.cs:line 38
at System.Threading.QueueUserWorkItemCallbackDefaultContext`1.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()

测试用例

Program.cs(创建100个线程调用handler.Add())

class Program
{
    private const int NUM_THREADS = 100;
    private static PayloadAccumulationHandler handler = new();

    static void Main(string[] args)
    {
        ThreadStart ts = new(RunMe);
        Thread[] threads = Enumerable.Range(0, NUM_THREADS)
            .Select(_ => new Thread(ts))
            .ToArray();

        foreach (Thread t in threads)
        {
            t.Start();
        }

        foreach (Thread t in threads)
        {
            t.Join();
        }
    }

    private static void RunMe()
    {
        int threadId = Environment.CurrentManagedThreadId;
        handler.Add("microsoft", "de", (uint)(threadId % 3));
    }
}

PayloadAccumulationHandler.cs(使用SynchronizationContext.Post())

public class PayloadAccumulationHandler
{
    private static readonly SynchronizationContext SyncContextTest = SynchronizationContext.Current ?? new SynchronizationContext();

    private static Dictionary<string, uint> dict = [];

    public void Add(string brand, string country, uint useCaseId)
    {
        Console.WriteLine($"Add({brand}, {country}, {useCaseId})");
        string key = $"{brand}/{country}/{useCaseId}";
        SyncContextTest.Post(AddCallback, key);
    }

    public void AddCallback(object? state)
    {
        int threadId = Environment.CurrentManagedThreadId;

        if (state is string key)
        {
            Console.WriteLine($"AddCallback: {threadId} {key}");

            if (dict.TryGetValue(key, out uint value))
            {
                dict[key] = value + 1;
            }
            else
            {
                dict[key] = 1; // 抛出System.InvalidOperationException!
            }
        }
    }
}

疑问

我特意用Dictionary而非ConcurrentDictionary,目的是验证AddCallback是否在同一线程执行,但实际并非如此。我是否操作有误,或是存在错误假设?

我原本期望SynchronizationContext.Post()类似Android Handler,能确保回调在同一线程执行。另外,控制台应用中SynchronizationContext.Current为null,新建的实例该如何配置才能让回调在单线程执行?


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:26:21