Запуск асинхронного процесса и ожидание его завершения - PullRequest
36 голосов
/ 04 марта 2009

Я новичок в модели потоков в .net. Что бы вы использовали для:

  1. запустить процесс, который обрабатывает файл (process.StartInfo.FileName = fileName;)
  2. ждать, пока пользователь не закроет процесс ИЛИ не покинуть поток через некоторое время
  3. если пользователь закрыл процесс, удалите файл

Запуск процесса и ожидание должны выполняться в потоке, отличном от основного потока, поскольку эта операция не должна влиять на приложение.

Пример:

Мое приложение создает HTML-отчет. Пользователь может щелкнуть правой кнопкой мыши где-нибудь и сказать «Просмотреть отчет» - теперь я извлекаю содержимое отчета во временный файл и запускаю процесс, который обрабатывает HTML-файлы, то есть браузер по умолчанию. Проблема в том, что я не могу очистить, то есть удалить временный файл.

Ответы [ 5 ]

61 голосов
/ 04 марта 2009

"а ожидание должно быть асинхронным" - я не пытаюсь быть смешным, но разве это не противоречие в терминах? Однако, поскольку вы запускаете Process, событие Exited может помочь:

ProcessStartInfo startInfo = null;
Process process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += delegate {/* clean up*/};

Если вы действительно хотите подождать (время ожидания и т. Д.), Тогда:

if(process.WaitForExit(timeout)) {
    // user exited
} else {
    // timeout (perhaps process.Kill();)
} 

Для ожидания асинхронного, возможно, просто использовать другой поток?

ThreadPool.QueueUserWorkItem(delegate {
    Process process = Process.Start(startInfo);
    if(process.WaitForExit(timeout)) {
        // user exited
    } else {
        // timeout
    }
});
18 голосов
/ 30 июля 2013

Добавление расширенной альтернативы этому старому вопросу. Если вы хотите дождаться завершения процесса, не блокируя какой-либо поток и по-прежнему поддерживая таймауты, попробуйте следующее:

    public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
    {
        ManualResetEvent processWaitObject = new ManualResetEvent(false);
        processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false);

        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

        RegisteredWaitHandle registeredProcessWaitHandle = null;
        registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject(
            processWaitObject,
            delegate(object state, bool timedOut)
            {
                if (!timedOut)
                {
                    registeredProcessWaitHandle.Unregister(null);
                }

                processWaitObject.Dispose();
                tcs.SetResult(!timedOut);
            },
            null /* state */,
            timeout,
            true /* executeOnlyOnce */);

        return tcs.Task;
    }

Опять же, преимущество этого подхода по сравнению с принятым ответом состоит в том, что вы не блокируете никакие потоки, что снижает нагрузку на ваше приложение.

4 голосов
/ 04 марта 2009

Попробуйте следующий код.

public void KickOffProcess(string filePath) {
  var proc = Process.Start(filePath);
  ThreadPool.QueueUserWorkItem(new WaitCallBack(WaitForProc), proc);
}

private void WaitForProc(object obj) {
  var proc = (Process)obj;
  proc.WaitForExit();
  // Do the file deletion here
}
0 голосов
/ 04 марта 2009

Вы можете использовать событие Exited в классе процесса

ProcessStartInfo info = new ProcessStartInfo();

info.FileName = "notepad.exe";
Process process = Process.Start(info);

process.Exited += new EventHandler(process_Exited);
Console.Read();

и в этом случае вы можете выполнять операции, которые вы упомянули

0 голосов
/ 04 марта 2009

Я бы, вероятно, не использовал отдельный процесс для открытия файла. Вместо этого я, вероятно, использовал бы фоновый поток (если бы я думал, что операция займет много времени и возможно заблокирует поток пользовательского интерфейса).

private delegate void FileOpenDelegate(string filename);

public void OpenFile(string filename)
{
   FileOpenDelegate fileOpenDelegate = OpenFileAsync;
   AsyncCallback callback = AsyncCompleteMethod;
   fileOpenDelegate.BeginInvoke(filename, callback, state);
}

private void OpenFileAsync(string filename)
{
   // file opening code here, and then do whatever with the file
}

Конечно, это не очень хороший рабочий пример (он ничего не возвращает), и я не показал, как обновляется пользовательский интерфейс (вы должны использовать BeginInvoke на уровне пользовательского интерфейса, поскольку фоновый поток не может обновить поток пользовательского интерфейса). Но этот подход, как правило, заключается в том, как я выполняю асинхронные операции в .Net.

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