Je suis nouveau sur le modèle de fil dans .net. Que feriez-vous pour:
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.
"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
}
});
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.
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
}
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.
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