Подтвердить что ты не робот

Как вызвать функцию в родительском потоке в .NET?

У меня есть библиотека классов .NET, содержащая класс с методом, который выполняет некоторую длительную операцию. Когда клиент вызывает этот метод, он должен выполнить длительную операцию в новом потоке, чтобы избежать блокировки вызывающего. Но как только метод заканчивается, он должен выполнить некоторый код в основном потоке. В приложении WinForms я мог бы использовать метод System.Windows.Forms.Control.Invoke, но это не мой случай. Итак, как я могу добиться этого в С#?

4b9b3361

Ответ 1

Я нашел простое решение проблемы:

Мой объект COM объявлен следующим образом:

public class Runner
{
    public void Run(string executable, object processExitHandler)
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            var p = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    FileName = executable
                }
            };
            p.Start();
            while (!p.HasExited)
            {
                Thread.Sleep(100);
            }

            state
                .GetType()
                .InvokeMember(
                    "call", 
                    BindingFlags.InvokeMethod, 
                    null, 
                    state, 
                    new object[] { null, p.ExitCode }
                );
        }, processExitHandler);
    }
}

И на моей странице HTML я использую его следующим образом:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><title>ActiveXRunner</title>    
    <script type="text/javascript">
    function runNotepad() {
        var ax = new ActiveXObject('ActiveXRunner.Runner');
        ax.Run('c:\\windows\\notepad.exe', h);
    }

    function h(exitCode) {
        alert('exitCode = ' + exitCode);
    }
    </script>
</head>
<body>
    <a href="#" onclick="runNotepad();">Run notepad and show exit code when finished</a>
</body>
</html>

Ответ 2

Вы можете вызвать функцию в определенном потоке с помощью объекта System.Windows.Threading.Dispatcher (из сборки WindowsBase).

Например:

public class ClassCreatedBySomeThread
{
    Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 

    public void SafelyCallMeFromAnyThread(Action a)
    {
       dispatcher.Invoke(a);
    }
} 

Ответ 3

Если поток должен иметь возможность выполнить некоторый бит кода (обычно в форме делегата), отправленный ему другим потоком, он должен будет в основном ждать этих инструкций. Что еще делает ваш основной поток? Не так сложно построить эквивалент цикла событий (в основном у вас будет очередь производителей/потребителей делегатов), но вам нужно знать, что вы не можете просто прервать основной поток и сказать "сделайте это сейчас".

Почему это должно выполняться в основном потоке?

Ответ 4

Нет никакого способа явно сделать запуск кода в определенном потоке (кроме потока, который использовался для создания элементов управления UI - это исключение), но если вы просто хотите вызывать код, отличный от кода, когда поток завершается, Вы используете делегатов.
Сначала объявите делегат с сигнатурой метода, который вы хотите запустить в новом потоке...

public delegate bool CheckPrimeDelegate(long n);

Затем в вашем коде создайте экземпляр делегата, вызовите его с помощью BeginInvoke (передайте любые необходимые ему параметры) и PASS делегат функции обратного вызова (OnChkPrimeDone)

class MyClassApp
{
   static void Main() 
   {
      CheckPrimeDelegate ckPrimDel = new CheckPrimeDelegate(Prime.Check);

      // Initiate the operation
      ckPrimDel.BeginInvoke(4501232117, new AsyncCallback(OnChkPrimeDone), null);

      // go do something else . . . .      
   }

   static void OnChkPrimeDone( IAsyncResult iAr)
   {
        AsyncResult ar = iAr as AsynchResult;
         CheckPrimeDelegate ckPrimDel = ar.AsyncDelegate as CheckPrimeDelegate;
         bool isPrime = ckPrimDel.EndInvoke(ar);
         Console.WriteLine(" Number is " + (isPrime? "prime ": "not prime");
   }
}

Когда это будет сделано, он вызовет функцию обратного вызова (OnChkPrimeDone)

Если вам явно нужно запустить эту функцию обратного вызова в потоке, который использовался для создания объекта COM Active-X, тогда проверьте переменную-оболочку .Net Managed Code, содержащую ссылку на этот объект... Если у нее есть метод InvokeRequired(), затем в вашей функции обратного вызова проверьте значение логического возврата этого метода.
Если он имеет метод InvokeRequired() и возвращает значение true, то объект active-X также выдает метод "BeginInvoke()". Затем создайте ДРУГОЙ делегат, заполненный той же функцией, и вызовите BeginInvoke на объекте Active-X, передав ему этот новый делегат... Затем он будет запускаться в том же потоке, который использовался для создания объекта Active-X

If (MyActiveXObject.InvokeRequired())
     MyActiveXObject.BeginInvoke(...);

Ответ 5

Нить не может просто выполнять материал в другом потоке. Самое близкое, что вы можете получить, - это поставить делегата в очередь для выполнения другого потока, но предполагается, что другой поток взаимодействует с этим.

В приложении WinForms основной цикл ищет такие сообщения в очереди на каждой итерации цикла.

Если вам просто нужно сообщить о завершении рабочего потока, вы можете использовать, например. флаг variabe. Если основной поток должен ждать завершения задания, используйте семафор или переменную условия (монитор).

Ответ 6

Здесь мой точный сценарий: у меня есть библиотека классов .NET, выставленная как объект COM (с использованием regasm.exe). COM-объект содержит метод, который запускает внешнее приложение (используя Process.Start). COM-объект используется в Internet Explorer. Поэтому моя веб-страница запускает внешнее приложение, и мне нужно найти способ передать ExitCode на веб-страницу.

Сначала я не запускал внешнее приложение в новом потоке и просто ждал, когда пользователь закроет приложение, а затем моя функция вернет ExitCode вызывающему. Но во время работы приложения IE не реагировал. Поэтому я решил запустить приложение в новом потоке, но теперь я больше не могу вернуть ExitCode.

COM-объект создается с помощью инструкции new ActiveXObject, и, к сожалению, javascript не поддерживает потоки событий, поэтому я не могу записать событие на С#, которое срабатывало при выходе приложения.