Есть ли причина, по которой поток ThreadPool может остановить вызов метода COM в другом модуле? - PullRequest
0 голосов
/ 07 августа 2020

Я загружаю сборку C# через COM из неуправляемого C ++, сборка обращается обратно в неуправляемый C ++ через интерфейс обратного вызова. Приложение C ++ использует сообщение Windows l oop и STA.

Интерфейсы определены в C#:

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("...")]
public interface IMyInterface
{
    void Start(ICallback callback);
    void Stop();
}

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("...")]
public interface ICallback
{
    void Message(string message);
}

В C ++ предоставляется реализация ICallback , а IMyInterface реализован в C#. Он запускает фон l oop, который периодически обращается к C ++ через обратный вызов, затем возвращает, оставляя l oop работающим:

[Guid("...")]
public class MyInterface : IMyInterface
{
    ICallback callback;
    public void Start(ICallback callback)
    {
        this.callback = callback;
        CallCallback("Test 1");

        StartAsyncMethod(); //deliberately doesn't await. 
    }

    void CallCallback(string message)
    {
        Thread thread = Thread.CurrentThread;
        Console.WriteLine($"'{message}'. Thread - Background: {thread.IsBackground}, Thread Pool: {thread.IsThreadPoolThread}, Thread ID: {thread.ManagedThreadId}");
        callback.Message(message);
    }

    async Task StartAsyncMethod()
    {
       var i=2;
       while(true)
       {
          await Task.Delay(5000);
          CallCallback($"Test {i++}");
       }
    } 

Все работает, за исключением того, что иногда вызывает метод обратного вызова stall - код C# остается в этой строке навсегда, метод C ++ никогда не запускается. Похоже, что виноваты вызовы метода async, поэтому я добавил запись в журнал деталей потока, мой вывод программы выглядит следующим образом:

'Test 1'. Тема - Фон: True, Пул потоков: False, ID потока: 3

'Test 2'. Тема - Фон: True, Пул потоков: True, ID потока: 6

Вызов callback.Message(message); останавливается, связанный со второй строкой выше, при вызове из фона l oop. Мой реальный код более сложен, но все обратные вызовы, сделанные непосредственно из Start, работают нормально; в тот момент, когда фон l oop попытался вызвать остановку кода C# (и код C ++ продолжает работать нормально, он просто никогда не получает обратный вызов).

Возможно ли, что поток пула потоков является как-то это вызывает? Ясно, что я использую его, чтобы получить доступ к асинхронной парадигме C#, но это долгоживущий Task, выполняющий программную жизнь l oop (которую я, очевидно, значительно упростил для этого вопроса). Что я мог попытаться решить, потому что я застрял на этом этапе!

Finer Detail

Приложение C ++ общается с другими приложениями C ++ через COM. Пока он этого не сделает - или если я не буду этого делать, мои обратные вызовы будут успешными . Сообщение oop довольно стандартно: MSG msg;

while (MsgWaitForMultipleObjects(1, &sm_hShutdown, FALSE, INFINITE, QS_ALLEVENTS) > WAIT_OBJECT_0)
{
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }
}   

C ++ использует CGlobalInterfaceTable и basi c COM, например, CoCreateInstance(ClassGUID, nullptr, CLSCTX_SERVER, GUIDIMyInterface, &pI); - ничего особенного.

Вдобавок , другая сборка C# COM использует тот же принцип c, но использует устаревшие потоки и не ожидающий TPL, и в ней нет этой проблемы.

** Обновление **

Итак, я только что попробовал это без каких-либо материалов типа TPL

    public void Start(ICallback callback)
    {
        this.callback = callback;
        CallCallback("Test 1");

        var thread = new Thread(() =>
        {
            while (true)
            {
                Thread.Sleep(5000);
                CallCallback("Thread Test");
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

Точно такая же проблема ... CallCallback зависает в потоке STA.

Гипотеза заключается в том, что я звоню обратно в COM из другого потока, который не владеет объектом, но создание потока STA означает, что я просто использую поток different STA. Поскольку это библиотека без точки входа, как может Я использую вызывающий поток для обратного вызова ... этот поток запускает Start, а затем исчезает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...