Проблемы с многопоточностью в C # от внешнего процесса - PullRequest
5 голосов
/ 12 декабря 2010

У меня есть простой код:

public void Run()
{
   var invokerThread = new Thread(new ThreadStart(RunOnBackground));
   invokerThread.Start();
}

private void RunOnBackground()
{
   Trace.WriteLine("hi");
   ...
}

К сожалению, при запуске этого кода (из стороннего процесса) поток на самом деле не запускается.В обозревателе процессов и в VS-отладчике я вижу, что поток создан и его состояние «Работает».

Квартира основного потока - STA, и я пробовал STA и MTA во внутреннем потоке.

Когда я добавляю к Run() методу в конце invokerThread.Join();, поток запускается.Но опять же, это не очень помогает.

Что мне не хватает?

Редактировать: Вот еще немного информации о хостинге кода - метод

Run()вызывается через COM-взаимодействие из процесса, который также является управляемой исполняемой сборкой (причина, по которой COM-взаимодействие используется, заключается в том, что все остальные компоненты в системе являются собственными).

Метод RunOnBackground() включает в себя еще немного кода после трассировки, и обычно его выполнение длится от 10 до 20 секунд, включая запуск другого процесса и ожидание его завершения.Также у меня есть некоторые другие области в коде, где я записываю некоторую отладочную информацию в Trace.Во время отладки кода Run() работает как обычно, и после invokerThread.Start(); состояние invokerThread равно «Выполняется» (хотя точки останова внутри метода RunOnBackground() не останавливаются).

Когда я добавляю invokerThread.Join() вВ конце метода Run() отладчик переходит к RunOnBackground() после Join().

Ответы [ 2 ]

3 голосов
/ 13 декабря 2010

Отсутствует важная информация о том, что на самом деле делает RunOnBackground () .В противном случае это хорошее совпадение с тем, что происходит, когда в рабочем потоке вы используете COM-объекты с многопоточной квартирой.COM автоматически выполняет маршализацию любого вызова метода для такого объекта из рабочего потока в поток STA, в котором он был создан.

Это может хорошо работать только тогда, когда поток STA соблюдает требования потоков STA.Он должен прокачать цикл сообщений и не может блокироваться.Нарушение этих правил делает очень вероятным взаимоблокировку, вызов рабочего потока не может быть завершен, пока поток STA не отправит маршалированный вызов.Верным признаком того, что это происходит, является то, что Thread.Join () решает проблему.Он накачивает цикл сообщений внутри CLR, когда он вызывается в потоке STA.

Чтобы диагностировать это, вам потребуется Debug + Windows + Threads, чтобы увидеть, что блокирует этот рабочий поток.Если мои предположения верны, они будут погребены глубоко в слесарном коде COM, ожидая завершения маршализованного вызова.Это можно увидеть, только включив отладку неуправляемого кода и настроив Microsoft Symbol Server, чтобы вы получили отладочные символы для программного кода и получили надежную трассировку стека.

Исправить это будет сложно.Вы не можете волшебным образом переключить коммутатор и запустить код в потоке, если он явно заявил, что не поддерживает многопоточность.Крайне важно, чтобы вы создали экземпляр COM-объекта в том же потоке, который вызывает его методы.И эта нить должна быть нитью STA.Проверьте этот пример кода для подхода.Если вы не контролируете создание COM-объекта, вы застряли.

0 голосов
/ 13 декабря 2010

Я могу сказать что-то глупое, но вот что я видел в MSDN Threads .

Посмотрите на раздел примеров в конце.

Вывод примера интересен, вы можете видеть, что созданный и запущенный поток начинает выполняться только тогда, когда основной поток выполняет Sleep (0) или Thread.Join ().

Похоже, что именно с тобой происходит, не так ли?

Возможно, попытайтесь с помощью Sleep (0) в вашем основном потоке, чтобы действительно запустить ваш рабочий поток.

Другой обходной путь - использование BackGroundWorker .

Как следует из названия, он работает на фоне и действительно прост в использовании. Это может быть очень полезно для вас.

...