简体   繁体   中英

C#: How do I wait for an event to be set?

I want to write a synchronous test that calls into some asynchronous product tasks.

In the example below, DoSomething() is called by a separate thread, and then it sets the SomethingCompleted event.

In my test code, how do I wait for SomethingCompleted to be set?

public event Action<Result> SomethingCompleted;

public void DoSomething()
{
    Something();

    this.SomethingCompleted(new Result("Success"));
}
using (var evt = new ManualResetEvent()) {
 Action<Result> handler = _ => evt.Set();
 SomethingCompleted += handler;
 evt.WaitOne();
 SomethingCompleted -= handler; //cut object reference to help GC
}

If required you can unsubscribe from the event after the wait has completed. That way the event will not keep the delegate and closure instance alive.

You can extract this into a reusable helper method/extension.

// test case
public void Test() 
{

   var yourObj = new YourObj();
   var done = false;
   Result result;
   yourObj.SomethingCompleted += (finalResult) => {
     result=finalResult; 
     done=true;
   };

   yourObj.DoSomething();

   while(!done) Thread.Sleep(200);

   if(result != theExpectedResult) kaboom();
}

What about subscribing to an event and "polling" the lambda until result comes available? This should work.

You're using the wrong type of event. The event you're using is a callback. In the code you supplied, the delegates attached to the SomethingCompleted event are going to be called on the same thread as DoSomething .

What you want is thread synchronization, using an event like AutoResetEvent or ManualResetEvent , which have nothing to do with the framework/language-level event that you're using. You could do something like this:

void MainThreadProc()
{
    // create thread synchronization event
    using (var evt = new ManualResetEvent(false))
    {
        // start background operation
        ThreadPool.QueueUserWorkItem(BackgroundThreadProc, evt);

        // this line will block until BackgroundThreadProc sets the event
        evt.WaitOne();
    }

    // resume main thread operations here
    ...
}

void BackgroundThreadProc(object o)
{
    // get event from argument
    var evt = (ManualResetEvent) o;

    // do the deed
    ...

    // set event to signal completion
    evt.Set();
}

This is just one of a number of different ways to do this. Alternatives include Parallel LINQ or the Task Parallel Library (both of which are best used with parallel operations, not just a single background operation). If you don't want to block the main thread, look at BackgroundWorker .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM