Введение
Это длинный вопрос! В начале вы найдете некоторую предысторию проблемы, затем примеры кода, которые были упрощены для представления, а затем Вопрос . Пожалуйста, читайте в любом порядке, который вам подходит!
Справочная информация
Я пишу часть Proof-of-Concept для приложения для связи с STA COM. Эта часть приложения требует выполнения в контексте однопотоковой квартиры (STA) для связи с указанным STA COM. Остальная часть приложения работает в контексте MTA.
Текущее состояние
То, что я до сих пор придумал, - это создание класса Communication , который содержит цикл while
, работающий в STA. Работа, которую необходимо передать в COM-объект, ставится в очередь извне в класс Communication через ConcurrentQueue
. Затем рабочие элементы удаляются из цикла while и работа выполняется.
Контекст кода
Класс связи
Это класс static
, содержащий цикл, предназначенный для запуска в состоянии STA и проверки необходимости какой-либо работы с помощью COM, и отправки работы обработчику.
static class Communication
{
#region Public Events
/// This event is raised when the COM object has been initialized
public static event EventHandler OnCOMInitialized;
#endregion Public Events
#region Private Members
/// Stores a reference to the COM object
private static COMType s_comObject;
/// Used to queue work that needs to be done by the COM object
private static ConcurrentQueue<WorkUnit> s_workQueue;
#endregion Private Members
#region Private Methods
/// Initializes the COM object
private static void InternalInitializeCOM()
{
s_comObject = new COMType();
if (s_comObject.Init())
{
OnCOMInitialized?.Invoke(null, EventArgs.Empty);
}
}
/// Dispatches the work unit to the correct handler
private static void HandleWork(WorkUnit work)
{
switch (work.Command)
{
case WorkCommand.Initialize:
InternalInitializeCOM();
break;
default:
break;
}
}
#endregion Private Methods
#region Public Methods
/// Starts the processing loop
public static void StartCommunication()
{
s_workQueue = new ConcurrentQueue<WorkUnit>();
while (true)
{
if (s_workQueue.TryDequeue(out var workUnit))
{
HandleWork(workUnit);
}
// [Place for a delaying logic]
}
}
/// Wraps the work unit creation for the task of Initializing the COM
public static void InitializeCOM()
{
var workUnit = new WorkUnit(
command: WorkCommand.Initialize,
arguments: null
);
s_workQueue.Enqueue(workUnit);
}
#endregion Public Methods
}
Рабочая команда
Этот класс описывает работу, которую необходимо выполнить, и любые аргументы, которые могут быть предоставлены.
enum WorkCommand
{
Initialize
}
Рабочая единица
Это перечисление определяет различные задачи, которые могут быть выполнены COM.
class WorkUnit
{
#region Public Properties
public WorkCommand Command { get; private set; }
public object[] Arguments { get; private set; }
#endregion Public Properties
#region Constructor
public WorkUnit(WorkCommand command, object[] arguments)
{
Command = command;
Arguments = arguments == null
? new object[0]
: arguments;
}
#endregion Constructor
}
Владелец
Это пример класса, которому принадлежит или порождает Communication
с COM и является абстракцией над Communication
для использования в остальной части приложения. .
class COMController
{
#region Public Events
/// This event is raised when the COM object has been initialized
public event EventHandler OnInitialize;
#endregion Public Events
#region Constructor
/// Creates a new COMController instance and starts the communication
public COMController()
{
var communicationThread = new Thread(() =>
{
Communication.StartCommunication();
});
communicationThread.SetApartmentState(ApartmentState.STA);
communicationThread.Start();
Communication.OnCOMInitialized += HandleCOMInitialized;
}
#endregion Constructor
#region Private Methods
/// Handles the initialized event raised from the Communication
private void HandleCOMInitialized()
{
OnInitialize?.Invoke(this, EventArgs.Emtpy);
}
#endregion Private Methods
#region Public Methods
/// Requests that the COM object be initialized
public void Initialize()
{
Communication.InitializeCOM();
}
#endregion Public Methods
}
проблема
Теперь рассмотрим метод Communication.StartCommunication()
, точнее, эту часть:
...
// [Place for a delaying logic]
...
Если эту строку заменить следующим:
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);
// OR
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(true);
во время проверки окончательная остановка - Communication.InternalInitializeCOM()
квартира нити кажется MTA .
Однако, если логика задержки изменяется на
Thread.Sleep(100);
метод CommunicationInternalInitializeCOM()
, кажется, выполняется в состоянии STA .
Проверка была сделана Thread.CurrentThread.GetApartmentState()
.
Вопрос
Может кто-нибудь объяснить мне, почему Task.Delay
нарушает состояние STA? Или я делаю что-то еще, что здесь не так?
Спасибо!
Спасибо, что уделили все это время, чтобы прочитать вопрос! Хорошего дня!