SendOrPostCallback не запускается в том же потоке при вызове SynchronizationContext.Post()C#

Место общения программистов C#
Ответить
Anonymous
 SendOrPostCallback не запускается в том же потоке при вызове SynchronizationContext.Post()

Сообщение Anonymous »

Для моего вопроса я подготовил скрипт C#, но, к сожалению, он не показывает проблему.
Однако, если вы скомпилируете и запустите мой код, указанный ниже, в 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));
}
}
А в PayloadAccumulationHandler.cs я пытаюсь использовать SynchronizationContext.Post():

Код: Выделить всё

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!
}
}
}
}
Dictionary имеет тип Dictionary, а не ConcurrentDictionary, потому что я хотел убедиться, что код в методе AddCallback выполняется в том же самом нить.
Но это не так! Я делаю что-то не так или, может быть, предполагаю что-то не так?
Я ожидал, что SynchronizationContext.Post() будет действовать аналогично обработчикам Android, где вы отправляете обратные вызовы в обработчик, и он гарантирует, что их код выполняется в одном потоке.
Кроме того, я заметил, что SynchronizationContext.Current имеет значение null, а новый SynchronizationContext() запускается в моем консольное приложение. Должен ли я каким-то образом указать/настроить этот новый экземпляр на использование одного потока для выполнения обратных вызовов?

Подробнее здесь: https://stackoverflow.com/questions/790 ... onizationc
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»