如何等到事件在c#中处理
Posted
技术标签:
【中文标题】如何等到事件在c#中处理【英文标题】:How to wait until event is handled in c# 【发布时间】:2020-07-05 18:34:31 【问题描述】:假设我们有这样的代码:
public class MyProcess
private Process _process;
public event EventHandler OnFinish;
public MyProcess(string pathToExe)
_process = new Process();
_process.StartInfo.FileName = pathToExe;
public int Start()
_process.Exited += _processExited;
_process.Start();
return _process.Id;
public void Stop()
_process.Stop();
private void _processExited(object sender, EventArgs e)
OnFinish?.Invoke();
public class MyProgram
private static int stoppedProcs = 0;
public static void Main(string[] args)
var proc = new MyProcess("foo.exe");
proc.OnFinish += proc_OnFinish;
proc.Start();
proc.Stop();
//Wait here to display 1 instead of 0
Console.WriteLine(stoppedProcs);
private static void proc_OnFinish(object sender, EventArgs e)
stoppedProcs++;
我创建并启动进程。然后我想停止它并仅在处理完事件后继续。问题是我不会自己调用事件,因为它是由操作系统完成的。如何理解事件处理程序已完成?
【问题讨论】:
你的Process
是System.Diagnostics.Process
吗?我在System.Diagnostics.Process
中找不到Stop
。
如果您想坚持事件模式,请查看ManualResetEvent 或ManualResetEventSlim。不过,您也可以切换到基于任务的异步 (TAP)。
@LouisGo 是的。我的错误,我使用 Kill()
@LouisGo 我不明白我可以在这里等待什么。我不调用异步方法。我杀死了一个进程,操作系统发出该进程退出的信号,然后调用处理程序
@Alexander 抱歉,Fildor 的建议更适合您的情况,我将删除我的评论以防止混淆。
【参考方案1】:
假设您想同步等待(阻塞当前线程),只需通过您的类公开Process.WaitForExit
方法。
public void WaitForExit()
_process.WaitForExit();
【讨论】:
【参考方案2】:扩展提到 Task
和 await
东西的答案
如果您想将等待变成异步流,您可以将Exited
事件与TaskCompletionSource
结合起来,如下所示:
public static class ProcessExtension
public static Task WaitForExitAsync(this Process proc, Action callback)
return new WaitForExitExt(proc, callback).WaitTask;
class WaitForExitExt
private TaskCompletionSource<int> _tcs;
private Action _callback;
public WaitForExitExt(Process proc, Action callback)
_callback = callback;
proc.EnableRaisingEvents = true;
_tcs = new TaskCompletionSource<int>();
if (proc.HasExited)
_tcs.TrySetResult(proc.ExitCode);
else
proc.Exited += OnProcessExited;
// Process already exited by the time the handler was attached.
if (proc.HasExited)
proc.Exited -= OnProcessExited;
_tcs.TrySetResult(proc.ExitCode);
public Task WaitTask => _tcs.Task;
private void OnProcessExited(object sender, EventArgs e)
var proc = (Process)sender;
proc.Exited -= OnProcessExited;
_callback();
_tcs.TrySetResult(proc.ExitCode);
上述扩展能够在进程退出后运行callback
,并且保证在任务完成之前运行callback
,因此您可以在代码中使用它:
var process = new Process();
process.StartInfo.FileName = "notepad.exe";
process.EnableRaisingEvents = true;
process.Start();
var task = process.WaitForExitAsync(() => Console.WriteLine("Exited"));
process.Kill();
await task;
Console.WriteLine("Task finished");
输出是:
Exited
Task finished
【讨论】:
【参考方案3】:感谢大家回答我的问题。
通过@Fildor 建议使用ManualResetEvent
,问题很容易解决
public class MyProgram
private static int stoppedProcs = 0;
private static ManualResetEvent mre = new ManualResetEvent(false);
public static void Main(string[] args)
var proc = new MyProcess("foo.exe");
proc.OnFinish += proc_OnFinish;
proc.Start();
proc.Stop();
//Wait here to display 1 instead of 0
mre.WaitOne();
mre.Reset();
Console.WriteLine(stoppedProcs);
private static void proc_OnFinish(object sender, EventArgs e)
stoppedProcs++;
mre.Set();
【讨论】:
这个解决方案可能很简单,但它是否健壮?如果在mre.WaitOne()
行之前调用proc_OnFinish
处理程序会发生什么?是否可以保证这永远不会发生?
@TheodorZoulias,好问题。据我了解,这无关紧要。如果处理程序在mre.WaitOne()
之前调用并调用mre.Set()
则不会阻塞主线程。
你说得对,我忘记了!所以你没有竞争条件,前提是proc_OnFinish
只会运行一次。 stoppedProcs++
行表明情况可能并非如此!顺便看看Interlocked.Increment
线程安全递增的方法。以上是关于如何等到事件在c#中处理的主要内容,如果未能解决你的问题,请参考以下文章