#c# #multithreading #events #vb6
#c# #многопоточность #Мероприятия #vb6
Вопрос:
Я создал многопоточную C # COM-сборку, я использовал ее из VB6.
C # -COM может запускать события из нескольких потоков, я создал невидимый From-объект и использую его для синхронизации всех событий перед их созданием.
if (myForm.InvokeRequired() )
{
delOnMessage myDelegate = new delOnMessage(Message_received);
myForm.Invoke(myDelegate, new object[] { null, null });
}
else
{
RaiseMyEvent();
}
Но если VB6-Code находится внутри обработчика событий и вызывает некоторые методы COM-объекта, это может привести к новому событию.
Private Sub m_SomeClass_SomeEvent(obj As Variant)
COMobject.SendAnAnswer() ' This produces a new event
End Sub
В этом случае часть системы событий перестает работать, что удивительно, основное приложение VB6 все еще работает.
РЕДАКТИРОВАТЬ: более подробно
Если C #-COM получил сообщение (от CAN-Bus-Thread), оно создает событие, в некоторых случаях VB6 вызывает метод C # -COM, который создает событие, это событие также достигается VB6.
Но тогда поток CAN-Bus, похоже, заблокирован, так как больше сообщений не получено (до перезапуска программы).
Но могут произойти и другие события.
Поток CAN-Bus — это бесконечный цикл для получения сообщения и запуска события.
У меня два вопроса:
Правильный ли мой способ синхронизации?
Возможно ли без изменения VB6-кода заставить его работать?
Комментарии:
1. Что именно означает «часть системы событий перестает работать»? Потоки C # выходят из строя, блокируются навсегда или что-то еще? Я предполагаю, что по крайней мере форма C # была создана в основном потоке VB6? (Кроме того, обычно для вызова достаточно просто создать экземпляр элемента управления вместо полномасштабной формы).
2. Спасибо за экземпляр элемента управления, будет достаточно подсказки. Я отредактировал вопрос, чтобы сделать «часть системы событий перестает работать» более понятной
3. Блокирует ли поток шины CAN при получении сообщения или отправке события? Когда он заблокирован, можете ли вы посмотреть на стек вызовов и посмотреть, есть ли предыдущий элемент управления. Вызов находится в стеке?
Ответ №1:
Я создал невидимый из-объект
Это звучит как проблема. Использование InvokeRequired является опасным антишаблоном. Это особенно опасно для VB6, его среда выполнения сильно нарушила обработку потоков. Вы знаете, что код вызывается из рабочего потока, используйте InvokeRequired только для проверки того, что форма, которую вы используете для синхронизации, находится в надлежащем состоянии, чтобы сделать это правильно:
if (!myForm.InvokeRequired()) {
throw new InvalidOperationException("Synchronization window not created");
}
delOnMessage myDelegate = new delOnMessage(FireMessageReceivedEvent);
myForm.BeginInvoke(myDelegate, new object[] { null, null });
Велика вероятность, что это исключение выдаст, создать невидимую форму не так просто. Вы можете принудительно создать свойство Handle формы, прочитав его свойство Handle . Или путем переопределения его метода SetVisibleCore(), чтобы форма оставалась невидимой:
protected override void SetVisibleCore(bool value) {
if (!this.IsHandleCreated) {
this.CreateHandle();
value = false;
}
base.SetVisibleCore(value);
}
Однако очень важно, чтобы вы вызывали метод Show() этой формы в основном потоке. Он все равно не будет работать корректно, если вы создадите форму в своем рабочем потоке. Нет простого способа проверить это в вашем коде. Используйте отладчик и окно Debug Windows Threads, чтобы проверить это.
И последнее, но не менее важное: отдавайте предпочтение BeginInvoke() вместо Invoke() . Это имеет гораздо меньшие шансы на создание взаимоблокировки. Это может вызвать проблемы само по себе, однако ваш рабочий поток, возможно, потребуется ограничить, чтобы он не заполнял основной поток запросами invoke.
Комментарии:
1. Я создаю свою невидимую форму
public class DebugForm : Form { public DebugForm(bool visible) { InitializeComponent(); this.Visible = visible; }
отладки, при запуске программы она видна в течение короткого времени, это работало последние годы. Почему это должно иметь значение, в каком потоке я его создаю?2. Потому что это приведет к маршалированию вызова в неправильный поток. Это должен быть основной поток VB6, иначе он будет работать неправильно.
3. К счастью, форма создается в контексте VB6-thread. Кстати. Я не могу использовать
BeginInvoke()
, поскольку это необходимо для синхронизации VB6 (и моих собственных обработчиков событий), чтобы находиться в определенном состоянии в момент выполнения обработчика событий4. Использование InvokeRequired является опасным антишаблоном. но какой здесь был бы правильный шаблон?
5. Я бы не сказал, что «среда выполнения VB6 сильно нарушила обработку потоков», она просто не предназначена для свободной обработки потоков.
Ответ №2:
В зависимости от характера события может быть достаточно просто переключиться с Invoke
на BeginInvoke
, чтобы оно было выгружено в очередь сообщений (без блокировки, поэтому нет взаимоблокировки). Удобно, что Control.BeginInvoke
(в отличие от Delegate.BeginInvoke
) не требует от вас вызова EndInvoke
, поэтому вы можете использовать это в порядке исключения.
Однако у меня может возникнуть соблазн отказаться от дополнительной работы:
myForm.BeginInvoke((MethodInvoker)RaiseMyEvent);
(т. е. переход непосредственно к RaiseMyEvent
)
Комментарии:
1. Необходимо, чтобы события выполнялись синхронно, чтобы получить определенное поведение, поэтому я могу использовать только
Invoke