Как вызвать функцию в родительском потоке в .NET? - PullRequest
23 голосов
/ 06 ноября 2008

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

Ответы [ 6 ]

26 голосов
/ 29 июля 2010

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

Например:

public class ClassCreatedBySomeThread
{
    Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 

    public void SafelyCallMeFromAnyThread(Action a)
    {
       dispatcher.Invoke(a);
    }
} 
12 голосов
/ 07 ноября 2008

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

Мой 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>
11 голосов
/ 06 ноября 2008

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

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

4 голосов
/ 07 ноября 2008

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

public delegate bool CheckPrimeDelegate(long n);

Затем в своем коде создайте экземпляр делегата, вызовите его с помощью BeginInvoke (передавая любые необходимые параметры) и ПРОЙДИТЕ делегат функции обратного вызова (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(...);
2 голосов
/ 06 ноября 2008

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

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

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

2 голосов
/ 06 ноября 2008

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

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

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

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