web-dev-qa-db-fra.com

Le processus asynchrone démarre et attend la fin

Je suis nouveau sur le modèle de fil dans .net. Que feriez-vous pour: 

  1. démarrer un processus qui gère un fichier (process.StartInfo.FileName = fileName;)
  2. attend que l'utilisateur ferme le processus OR abandonne le fil après un certain temps
  3. si l'utilisateur a fermé le processus, supprimez le fichier

Le démarrage du processus et l'attente doivent être effectués sur un autre thread que le principal, car cette opération ne doit pas affecter l'application.

Exemple: 

Mon application génère un rapport HTML. L'utilisateur peut cliquer quelque part avec le bouton droit de la souris et dire "Afficher le rapport". Je récupère maintenant le contenu du rapport dans un fichier temporaire et lance le processus de gestion des fichiers HTML, c'est-à-dire le navigateur par défaut. Le problème est que je ne peux pas nettoyer, c’est-à-dire supprimer le fichier temporaire.

36

"et attendre doit être asynchrone" - Je n'essaie pas d'être drôle, mais n'est-ce pas une contradiction dans les termes? Toutefois, étant donné que vous démarrez une Process, l'événement Exited peut vous aider:

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

Si vous voulez réellement attendre (timeout, etc.), alors:

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

Pour attendre async, utilisez peut-être simplement un autre thread?

ThreadPool.QueueUserWorkItem(delegate {
    Process process = Process.Start(startInfo);
    if(process.WaitForExit(timeout)) {
        // user exited
    } else {
        // timeout
    }
});
60
Marc Gravell

Ajouter une alternative avancée à cette vieille question. Si vous souhaitez attendre la fin d'un processus sans bloquer aucun thread et toujours prendre en charge les délais d'attente, procédez comme suit:

    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;
    }

Encore une fois, l'avantage de cette approche par rapport à la réponse acceptée est que vous ne bloquez aucun fil, ce qui réduit les frais généraux de votre application.

18
Chris Gillum

Essayez le code suivant. 

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
}
4
JaredPar

Je n'utiliserais probablement pas un processus distinct pour ouvrir un fichier. Au lieu de cela, j'utiliserais probablement un thread d'arrière-plan (si je pensais que l'opération prendrait beaucoup de temps et pourrait bloquer le thread d'interface utilisateur). 

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
}

Bien sûr, ce n’est pas un bon exemple (il ne renvoie rien) et je n’ai pas montré comment l’interface utilisateur était mise à jour (vous devez utiliser BeginInvoke au niveau de l’UI car un thread en arrière-plan ne peut pas mettre à jour le thread UI). Mais cette approche est généralement la manière dont je traite les opérations asynchrones en .Net. 

0
Chris Holmes

Vous pouvez utiliser l'événement Exited dans la classe Process.

ProcessStartInfo info = new ProcessStartInfo();

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

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

et dans ce cas, vous pouvez gérer les opérations que vous avez mentionnées

0
Pablo Retyk