Can I make async components synchronous?

I am currently trying to fix an initialization problem that stems from the assumption that all subcomponents are initialized synchronously.

The user interface creates an instance of a class with its own user interface. It looks something like this:

ConfWizard cf = new ConfWizard();
cf.ShowDialog();

      

The problem is that the ConfWizard class uses a different class that initializes asynchronously, but must be ready before ShowDialog is called to function properly. ConfWizard code looks something like this:

public ConfWizard()
{
    helper = new HelperClass
    helper.ReadyEvent += new HelperClass.ReadyEventHandler(this.helper_ReadyEvent)
    helper.StartUp();
    // Do more initialization using properties of hc
}

private helper_ReadyEvent()
{
    //HelperClass is ready to use
}

      

Because helper properties cannot be set until ReadyEvent is raised, the current constructor is usually not initialized correctly. It might seem obvious to put the rest of the initialization in helper_ReadyEvent, but this will cause the constructor to return before the object is ready for use. Because the classes that use the ConfWizard object assume that when the constructor returns the object, it is ready for use, premature return is not desirable.

Unfortunately, I cannot change the HelperClass, so I need to mask its asynchronous behavior so that the ConfWizard class can be used synchronously.

I tried to use the ManualResetEvent object (by calling Set in the event handler), but the WaitOne calls block and therefore the event is not handled by the application app.

Any ideas on how to achieve this in .NET1.1?

UPDATE - Aug 21, 2009
I had time to experiment today and here is what I found.

WaitOne - if given a long enough timeout will work every time, just delaying the application. Unfortunately this timeout must be at least 5 seconds (longer than I have to wait). Without timeout, it still freezes. The event that triggers the set just never happens.

Sleeper is the same as WaitOne, in which it will work with a sufficiently long timeout.

Threading - I don't want the interface to continue until initialization is done because the UI behavior changes as a result of the initialization. However, splitting the initialization of the HelperClass object into a separate thread and calling Thread.Join to suspend the main thread.

So the solution to the problem appears to be using multiple threads correctly.

+2


source to share


5 answers


You hack into it and add a read-only property in the configuration wizard that's set to true when the helper_ReadyEvent delegate is called. Then you can poll the property and show the dialog as soon as the form is ready.

ConfigWizard wiz = new ConfigWizard();
while (!wiz.Ready) System.Threading.Thread.Sleep(2000);
wiz.ShowDialog();

      



Or could you not initialize the helper class before the ConfigWizard was initialized? Then could you just provide a reference to a helper class that has been initialized into a config form via the class constructor? Given the number of answers here, it seems to me there are many ways you could accomplish the task.

+1


source


I don't understand how it ManualResetEvent

doesn't work in your case. If you create one of them after instantiation HelperClass

and Set

in yours ReadyEvent

, then all you have to do is add it WaitOne

at the bottom of your constructor ConfWizard

. Yes, it WaitOne

will block, but this is the behavior (that your constructor ConfWizard

doesn't return until everything is ready) you want, right?



0


source


My first thought was "Use a wait handle", but as you said at the end of your post, this won't work as the event will try to raise the UI thread, but block it while it waits for the UI thread.

(I assume this doesn't work. If it is hoisted up on a background thread - just use ManualResetEvent to signal that the UI thread is ready.)

One alternative solution is to render the form even if the helper is not ready, and redirect all the actions of the helper class to an action queue that is processed when they are ready:

private Queue actions = new Queue();

public void DoSomethingToHelper()
{
   if(!helperClass.IsReady())
   {
      Action work = new Action(DoSomethingToComponent);
   }
   else
   {
       // Real work here.
   }
}

      

Then, when it's ready, you go through and process all the actions:

private helper_ReadyEvent()
{
    foreach (Action action in actions)
    {
        action.Invoke();
    }
    actions.Clear();
}

      

0


source


I tried to use the ManualResetEvent object (by calling Set in the event handler), but the WaitOne calls block and therefore the event is not handled by the application app.

This should mean that either ReadyEvent is called on the same thread as the ctor, or that the HelperClass needs a UI thread. This is a bit of a pickle since you can't delay the constructor.

If the HelperClass just needs to handle some window message, you can inject Application.DoEvents .

class ConfWizard {
    private ManualResetEvent _init = new ManualResetEvent(false);

    public ConfWizard() {
       var h = new HelperClass();
       h.ReadyEvent += this.helper_ReadyEvent;
       h.StartUp();

       do {
          Application.DoEvents();
       } while (!_init.WaitOne(1000));
   }    

   private void helper_ReadyEvent() {
       _init.Set();
   }
}

class HelperClass {
   public event Action ReadyEvent;

   public void StartUp() {
      ThreadPool.QueueUserWorkItem(s => {
         Thread.Sleep(10000);
         var e = this.ReadyEvent;
         if (e != null) e();
      });
   }
}

      

Otherwise - I think you will have to either create asynchronously through the factory, or just deal with the fact that it HelperClass

might not be ready and rework or disable the UI if needed.

Edit:

Is there [DoEvents] as scary as in VB6?

For many people , yes . But, IMO, it (as usual) depends on the scenario. You have to be careful with it because of the possible re-entry - if you are running as a result of a windowed message then you might have to guard against this.

FWIW, the most common use of DoEvents is to redraw the screen due to long running time that would otherwise freeze the UI. In this case, yes, .NET gives you many better ways to deal with streams. But if you just want to get the UI thread (which you do), I ( and others ) see no problem with some carefully and carefully placed DoEvent calls. And honestly, I think this is the least complex of your options (without rewriting HelperClass, of course).

0


source


why don't you connect

helper.StartUp ();

to helper.ReadyEvent also?

-1


source







All Articles