Однако, если вы скомпилируете и запустите мой код, указанный ниже, в Visual Studio 2022 или VS Код, затем выдается следующее исключение:
Код: Выделить всё
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.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at System.Collections.Generic.Dictionary`2.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));
}
}
Код: Выделить всё
public class PayloadAccumulationHandler
{
private static readonly SynchronizationContext SyncContextTest = SynchronizationContext.Current ?? new SynchronizationContext();
private static Dictionary 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; // throws System.InvalidOperationException!
}
}
}
}
Но это не так! Я делаю что-то не так или, может быть, предполагаю что-то не так?
Я ожидал, что SynchronizationContext.Post() будет действовать аналогично обработчикам Android, где вы отправляете обратные вызовы в обработчик, и он гарантирует, что их код выполняется в одном потоке.
Кроме того, я заметил, что SynchronizationContext.Current имеет значение null, а новый SynchronizationContext() запускается в моем консольное приложение. Должен ли я каким-то образом указать/настроить этот новый экземпляр на использование одного потока для выполнения обратных вызовов?
Подробнее здесь: https://stackoverflow.com/questions/790 ... onizationc
Мобильная версия