AsyncLocal & CallContext - Как я могу получить как асинхронные *, так и * удаленные гарантии логического контекста?
Я пытаюсь разработать библиотеку, которая реализует свободно определенный шаблон Ambient Context. Мне нужно учитывать как высокую степень параллелизма, так и удаленного взаимодействия (.NET Framework 4.6.2
). Кажется, у меня есть 2 варианта: AsyncLocal<T>
а такжеCallContext
, Я не верю ни одному из этих соображений.
Рассмотрим следующий пример, который демонстрирует удаленную половину моей проблемы:
class Program
{
static string hello = "Hello from '{0}'! \n\tAsyncID: {1} \n\tContextID: {2} ";
static void Main(string[] args)
{
OperationManager.CallContextOperationID = Guid.NewGuid();
OperationManager.AsyncLocalOperationID = Guid.NewGuid();
AppDomain other = AppDomain.CreateDomain("remote");
SayHello();
other.DoCallBack(SayHello);
Console.ReadKey();
}
private static void SayHello()
{
string statement = string.Format(hello, AppDomain.CurrentDomain.FriendlyName,
OperationManager.AsyncLocalOperationID,
OperationManager.CallContextOperationID);
Console.WriteLine(statement);
}
}
internal class OperationManager
{
private static string _slotName = "HelloWorld";
private static readonly AsyncLocal<Guid> _operationID = new AsyncLocal<Guid>();
public static Guid AsyncLocalOperationID
{
get => _operationID.Value;
set => _operationID.Value = value;
}
public static Guid CallContextOperationID
{
get => (Guid)CallContext.LogicalGetData(_slotName);
set => CallContext.LogicalSetData(_slotName, value);
}
}
Этот класс создает два идентификатора GUID, сохраняя один в локальном асинхронном режиме, а другой в контексте логического вызова. Затем он запускает новый AppDomain и печатает значения из текущего и удаленного домена.
Пример вывода из этой программы показывает
Hello from 'ConsoleApp1.exe'!
AsyncID: 4c9e7c3a-fef8-4948-b0f0-896abe7dc2dd
ContextID: 4b479195-6fe8-43ae-a753-2fb3ccc57530
Hello from 'remote'!
AsyncID: 00000000-0000-0000-0000-000000000000
ContextID: 4b479195-6fe8-43ae-a753-2fb3ccc57530
И мы можем видеть, что CallContext сделал это через границу удаленного взаимодействия, в то время как AsyncLocal этого не сделал (не удивительно).
Проблема заключается в том, что я не верю, что смогу использовать CallContext в среде "простого параллелизма" (как изложено Стивеном Клири в этом посте) из-за того, что CallContext является общим для всех потоков. У Стивена есть отличный пример псевдокода в связанном решении, который я не буду здесь дублировать. Этот пример обрисовывает в общих чертах асинхронную часть моей проблемы.
Мои два варианта становятся
AsyncLocal<T>
: Работает в среде "простого параллелизма", но выходит за границы удаленного взаимодействия.
CallContext
Работает за пределами удаленного взаимодействия, но не работает в среде "простого параллелизма".
Есть ли здесь третий вариант, который мне не хватает?