|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Convert asynchronous to synchronousI have a method which I want to present as a synchronous call - you pass in your parameters, and the result as a return value. But within that method, I need to spin off a thread, wait for it to finish doing some stuff, and return a value to the calling method. The problem is that my method may be called on the gui thread (since it's out of my control where my method is called from), and the gui will become unresponsive while my method is running if I wait for the thread to finish using Thread.Join. So what I'm doing at the moment is a loop like this: while (Thread.IsAlive) { Thread.Sleep(100); Application.DoEvents(); } Now I know that using Application.DoEvents like that is horrible, but I can't see any other way to wait for the thread to finish before returning the value to the calling method. Sure, I could pass the thread a delegate to call when it's finished, but I want the whole process to appear to be synchronous to the calling method. Is there a better way to accomplish this? cj_junkt***@mail.com wrote:
> Now I know that using Application.DoEvents like that is horrible, but I Maybe you could open a modal dialog box with a progress bar on it or > can't see any other way to wait for the thread to finish before > returning the value to the calling method. Sure, I could pass the > thread a delegate to call when it's finished, but I want the whole > process to appear to be synchronous to the calling method. Is there a > better way to accomplish this? something similar? Otherwise I'd probably make the call asynchronous and add a callback.. Max cj_junkt***@mail.com wrote:
Show quote > Hi You're best off not trying to present a synchronous interface to a long > > I have a method which I want to present as a synchronous call - you > pass in your parameters, and the result as a return value. But within > that method, I need to spin off a thread, wait for it to finish doing > some stuff, and return a value to the calling method. The problem is > that my method may be called on the gui thread (since it's out of my > control where my method is called from), and the gui will become > unresponsive while my method is running if I wait for the thread to > finish using Thread.Join. So what I'm doing at the moment is a loop > like this: > > while (Thread.IsAlive) > { > Thread.Sleep(100); > Application.DoEvents(); > } > > Now I know that using Application.DoEvents like that is horrible, but > I can't see any other way to wait for the thread to finish before > returning the value to the calling method. Sure, I could pass the > thread a delegate to call when it's finished, but I want the whole > process to appear to be synchronous to the calling method. Is there a > better way to accomplish this? running function if it may be called from the GUI thread. Using Application.DoEvents as you've shown does simulate the effect, but only if it is in fact called from the GUI thread, and at the cost of having the GUI thread spin, needlessly burning CPU cycles. The problem is, there's no way in WinForms to start a new (non-polling) message loop and cause that message loop to exit when a desired event occurs. So, in this case, what you should do in your function is nothing - just make it synchronous. The GUI needs to take care of itself - which means never calling your long-running function on the GUI thread. So what's the GUI to do when it needs to call a long-running function? Use a worker thread. This is the technique that I use: I always have the "main form" of the application available throughout the app - your choice how to make it available (there are lots of ways). Within the main form (called "Shell" in the code below), I'll expose a public function like this: public void RunBackgroundTask(object s, WaitCallback item, WaitCallback cleanup) { // do any app-specific "lockout" work here // ... ThreadPool.QueueUserWorkItem( delegate(object state) { try { item(state); } catch (Exception e) { // do whatever you think appropriate to handle/log the exception Trace.WriteLine(string.Format("Internal error {0}:{1} [{2}]", e.GetType().FullName, e.Message, e.StackTrace)); } this.After_BackgroundItem(s, cleanup); }, s ); } private delegate void CallbackEventHandler(object state, WaitCallback callback); private void After_BackgroundItem(object state, WaitCallback cleanup) { if (InvokeRequired) { object[] oa = new object[] { state, cleanup }; Invoke(new CallbackEventHandler(After_BackgroundItem),oa); } else { // do any app-specific "unlock" work here // ... if (null != cleanup) cleanup(state); } } Then, where I want to do some async work (directly or indirectly, this will be called by a message handler called by the main window message loop): Shell.RunBackgroundTask( this, // This part is run by a worker thread delegate(object state) { // background task code here }, // This part is run in the UI thread after the task completes delegate(object state) { // UI cleanup code here } ); The C# 2.0 anonymous delegates let you easily partition the background worker code from the UI code without having to create a bunch of single-use functions. This keeps all of the code related to the function close together and (I think) makes it easier to understand - once you accept that the first anonymous block runs in a worker thread and the second runs in the GUI thread. Note that within the anonymous blocks of code, all local and member variables of the enclosing function and class are accessible, thanks to the closures that the C# compiler creates for you. You could also use the BackgroundWorker component that's new in WinForms 2.0. Of course, if you're stil on 1.x, you have little choice but to directly use ThreadPool and/or asynchronous delegate invocations (which internally use the thread pool). -cd
Other interesting topics
|
|||||||||||||||||||||||