How to set a timeout for a dynamically loaded method - c#

Description: I am using Type.InvokeMember to call a method from a dynamically loaded library. Sometimes, this method hangs and does not release its resources. In addition, the method does not take a timeout as a parameter.
How can I set a timeout for this method, STOP the method from running in the background, and move on to the next step?
Any help would be appreciated!

Props to #lightbricko as his methodology will give you the abort functionality you need.
As far as a timeout is concerned, it may be easier to provide a wrapper class for monitoring the time and invoking the solution on a separate thread.
Your question fails to mention if there is a return value, or a requirement for synchronicity. I'll present a solution with increasing levels of complexity depending on the requirements. Please respond to this thread if you need me to flesh out the more complex solutions.
Not in my IDE, so I whipped this up in Notepad. Its a bit dirty but should give you a skeleton to work from.
You can pass in the necessary parameters for invoking the method. For bonus points check out Action and Function delegates! :)
public class TimedRemoteInvocation
{
Timer myTimer;
Thread myThread;
public static RemoteInvocationTimer Invoke(..., int timeout)
{
return new RemoteInvocationTimer().InvokeMember(...)
}
private RemoteInvocationTimer InvokeMember( ..., int timeout )
{
myTimer = new Timer();
myTimer.Elapsed += new ElapsedEventHandler( TimeOutOccurred );
myTimer.Interval = timeout; //ex: 200ms
myThread = new Thread(
new ThreadStart(theTypeObj.InvokeMember(...));
myTimer.Start();
mythread.start();
return this;
}
public void ElapsedEventHandler( Object sender, ElapsedEventArgs e )
{
myThread.Abort()
}
}
If you need this to appear synchronous to the caller (that is, the caller needs to wait for completion) let me know and I'll update the code.
The idea would be for the caller to wait on a lock mutex.
That lock would be grabbed first by the TimedRemoteInvocation class, and released when the operation completes. Again I can provide more info if you need it.
TimedRemoteInvocation caller = TimedRemoteInvocation.Invoke(...)
lock(caller.sharedLock) {}
The final variation expects a result from the invoked method.
This can be easily captured but returning that value to the caller would either require
The caller to handle an event that declares a result
Should synchronicity be required, the result can be stored in a property of the TimedRemoteInvocation class (again the use of a mutex lock would be required).
TimedRemoteInvocation caller = TimedRemoteInvocation.Invoke(...)
res = caller.Result //The result property will grab/wait on the mutex and halt the caller.
There's a lot to unpack here and I know I'm glossing over things, so let me know if you need me to add to anything.

You can either spawn a new Thread and abort it when needed or you can create a new AppDomain and unload it when needed.
Using an AppDomain provides more isolation.

Related

Threadsafe properties

I'm trying to learn threading on C# but have got a bit stuck on how to handle properties.
Take for example my class NavigateIE which can only carry out a single action at a time. I thought if I had a property busy then I would know if the instance was busy outside the class.
class NavigateIE
{
public bool busy;
public void IEAction(string action)
{
busy = true;
var th = new Thread(() =>
{
try
{
//Do stuff
}
catch(Exception ex)
{
//report Exception
}
finally
{
busy = false;
}
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
}
However, as busy = false; is only ever called inside the thread then this doesn't work as navigateIE.busy is always true.
class MainElsewhere
{
private NavigateIE navigateIE = new NavigateIE();
private void Main()
{
if (!navigateIE.busy)
{
//navigateIE.busy always == true
}
}
}
I have 2 questions:
1) How do I set up the property so it's threadsafe and busy = false; is seen outside the class?
2) Is there a better way to do this?
Edit: Essentially NavigateIE is a class to handle a single instance of IE using Watin. I can only call a method in NavigateIE if there are no other methods running otherwsie a previous action has not completed. NavigateIE is called from a main method that is on a timer tick, hence why I need to check if the class is busy.
navigateIE.busy == false the first time but after the thread sets it back to false the main method still sees navigateIE.busy == true.
You are doing this fundamentally wrong. Yes, busy is pretty likely to be true, that thread will start running quickly on a modern multi-core processor. No actual guarantee, it is merely very common. Exactly when it turns back to false is highly unpredictable as well, depends what the thread does. Including never, a bool is not a synchronization primitive.
It is not clear to me what you are trying to achieve, but you don't actually need to use a busy flag at all. IE is an apartment-threaded COM object, it already automatically marshals the call to the STA thread that owns the object. Calling its Navigate() method from another thread is fine, no extra code is required to help. If you need a busy flag to indicate that the browser is working on a command then you'll need a flag that you set to true when you actually start navigating instead of when you start the thread. Leverage the code in this answer, perhaps.
I started to write an answer, but there are just a lot of variables here. (you should be locking, but really signalling might be a better solution, etc). I really think that rather than throwing a code snippet at you, a better answer would be do direct you to do a little more digging on .net threading.
Here's an ebook that might be helpful for you: http://www.albahari.com/threading/
How do I set up the property so [..] busy = false; is seen outside the class?
Your example doesn't work because busy needs to be marked volatile
Is there a better way to do this?
It sounds like all calls within NavigateIE always need to be serialized. If that's true, I would just put a lock statement in each thread-call
public object mutex = new object();
public void IEAction(string action)
{
var th = new Thread(() =>
{
lock(mutex)
{
//Serialzed code goes here
}
});
//etc.
}
Also, note that you rarely want to actually create your own Thread instance - you should either be using a BackgroundWorker, obtaining a thread from the ThreadPool, or using the new Tasks Parallel Library (TPL).
If you're unsure, the TPL is usually the best place to start.

Does the Timer(callback,...) callback function add to the stack? garbage collection Q with this

I have a function that needs to archive 90 day old emails every midnight. I created a class to handle this and here's the example:
public void processArchives()
{
initializeTimer();
}
private void initializeTimer()
{
var now = DateTime.Now;
var tomorrow = now.AddDays(1);
var durationUntilMidnight = tomorrow.Date - now;
var t = new Timer(o => { attemptArchivalProcess(); }, null, TimeSpan.Zero, durationUntilMidnight);
}
private void attemptArchivalProcess()
{
//perform archival
initializeTimer(); //re-start timer to process tomorrow
}
question is, will the repeated calls to initializeTimer cause a stack overflow (repeated function calls) or will it run fine 'forever'?
and should I be able to call processArchives() as a new thread, and keep the thread open or do I need some sort of loop after the init call something like:
while(!Program.Closing){ sleep(...); }
to keep it from being garbage collected?
I think you are close to your potential solution.
Timer
To answer your first question: As you already concluded the timer will elapse on it's delegate. The delegate will be executed on a separate thread and each timer elapse will get a brand new fresh own stack to execute on. Thus, endless timer elapsed events will never trigger a StackOverflowException.
Wait until forever?
An attempt to answer your second question: You don't have to write an endless loop to keep your application alive. But, you could do that, it all depends on what you need for your application. Weigh the pros and cons.
Luckily there are more solutions possible (there is no right or wrong, weigh them out to fulfill your needs)
A shot list of solutions you could concider:
Console.ReadLine()
If you have a console application, you could simply wait for user input. The main thread will wait forever without consuming processor power.
As suggested by Servy, create a scheduled task
That way you don't have to do anything at all to write endless loops. Your application will simply exit when it's done. Might not be the most handsome solution if you actually deploy this app to users.
Windows service
You could also go for a bit more mature solution and write a windows service (sounds more complicated than it is, it's pretty simple to write a basic windows service). That way you also don't have to bother about writing never ending loops, a windows service will run forever by design (unit you decide to stop it of course)
Alternative for never ending while loop - WaitHandle
You could also use a signal mechanism (e.g. with a AutoResetEvent) so that your main thread can Wait until a certain signal is set. That way you also don't have to actively wait (=not consume processor cycles).
You have tons of alternatives, it all boils down to your specific needs, which I can't decide for you. You can. :)
All those words, let's throw in an example. The unit test represents your application. The timer is a different type, namely the System.Timers.Timer. You can set that timer to AutoReset so that you don't have to create new timers.
Here the example, I hope it makes sense to you (if not, comment, maybe I can clarify)
private Timer _processTimer;
private AutoResetEvent _resetSignal;
[Test]
public void YourImaginaryMainApp()
{
const int interval = 24 * 60 * 60 * 1000; // every day
_resetSignal = new AutoResetEvent(false);
_processTimer = new Timer(interval)
{
AutoReset = true
};
_processTimer.Elapsed += ProcessTimerOnElapsed;
_resetSignal.WaitOne( /*infinite*/);
}
Edit x1 - meant "heap", not "stack" in second sentence... (d'oh!)
I don't think this will cause a stack overflow for a very simple reason. The line var t = new Timer(... creates a new object on the heap. The function pointer is kept internally within the object and should (in theory) never be added to the stack until it's actually called. When attemptArchivalProcess() is called, it in turns calls initializeTimer() (adding to the stack) but this then completes and exits as normal on the same thread (removing from the stack). When the Timer does kick in, it'll start off that 2-call entry to the stack.
Now, all this said, I know there is increased complexity going on under the hood with the stack but my point is that ultimately that you have 2 methods being called which then exit correctly - and should be cleaned up correctly when they exit.
Or at least that's my reasoning. I fully admit I'm open to correction on this though...
Here's my attempted resolution for this....
System.Threading.Timer timerFunc = null;
public void processArchives()
{
initializeTimer();
while (!CSEmailQueues.StoppingService) //is
Thread.Sleep(CSEmailQueues.sleeptime);
timerFunc.Dispose();
return;
}
private void initializeTimer()
{
var now = DateTime.Now;
var tomorrow = now.AddDays(1);
var durationUntilMidnight = tomorrow.Date - now;
if (timerFunc != null) timerFunc.Dispose();
timerFunc = new System.Threading.Timer(o => { attemptArchivalProcess(); }, null, TimeSpan.Zero, durationUntilMidnight);
}
private void attemptArchivalProcess()
{
//Do Work
initializeTimer(); //re-start timer to process tomorrow
}
So...this will dispose the timer object and create a new one each time it runs (as this timer only executes once). Plus being a class level variable, there is always a reference to the timer so the garbage collector doesn't dispose of it while i'm waiting for it to trigger.
Then all I have to do is create a thread that calls processArchives() from the onStart call for the service, and this should in essence run forever unless the onStop is called and sets StoppingService to true.
Also I am guessing I shouldn't have to worry about cross-thread use of timerFunc by the timer callback as there should never be more than 1 instance at any given time to access this object.

Will the threadpool queue a timer's callback function, sometimes scheduling more than one thread at the same time?

In the following code TimerRecalcStatisticsElapsed should only have one instance of it running. The worker methods that this callback invokes is made to run in sequence, with a maximum of one thread running at a time.
Question Part 1:
If the timer's callback runs an a threadpool thread (as opposed to running the callback on a separate thread), is it correct to say the the threadpool might queue and defer the thread for later execution based on conditions (MaxThreads reached, threadpool internal logic)?
Question Part 2:
Assuming it's possible for one timer callback to be queued for anything but immediate execution, does that mean that any number of thread callbacks may execute concurrently?
Question Part 3
Assuming part 2 is true, does that mean the code below can ever have more than one callback operating at the same time?
The reason I'm asking is because there are several thousand instances of this class running on a multi-CPU server. I'm also seeing data corruption consistent with an out-of-order operation of // Do Work Here.
Aside
// Do work here internally works with a System.Collections.Dictionary and edits the values of y. It also removes some keys for a subsequent function that is called serially. That function is missing keys (x) that were previously present in the first call. I think this is because there is a race condition with the final statement obj.cleanupdata()
public class SystemTimerTest
{
readonly System.Timers.Timer timerRecalcStatistics;
readonly System.Diagnostics.Stopwatch stopwatchForRecalcStatistics = new System.Diagnostics.Stopwatch();
public SystemTimerTest(TimeSpan range, DataOverwriteAction action)
{
int recalculateStatisticsEveryXMillseconds = 1000;
timerRecalcStatistics = new System.Timers.Timer(recalculateStatisticsEveryXMillseconds);
timerRecalcStatistics.AutoReset = true;
timerRecalcStatistics.Elapsed += new System.Timers.ElapsedEventHandler(TimerRecalcStatisticsElapsed);
timerRecalcStatistics.Interval = recalculateStatisticsEveryXMillseconds;
timerRecalcStatistics.Enabled = true;
this.maxRange = range;
this.hashRunningTotalDB = new HashRunningTotalDB(action);
this.hashesByDate = new HashesByDate(action);
this.dataOverwriteAction = action;
}
private void TimerRecalcStatisticsElapsed(object source, System.Timers.ElapsedEventArgs e)
{
stopwatchForRecalcStatistics.Start();
Console.WriteLine("The TimerRecalcStatisticsElapsed event was raised at {0}", e.SignalTime.ToString("o"));
// DO WORK HERE
stopwatchForRecalcStatistics.Stop();
double timeBuffer = GetInterval(IntervalTypeEnum.NearestSecond, e.SignalTime) - stopwatchForRecalcStatistics.ElapsedMilliseconds;
if (timeBuffer > 0)
timerRecalcStatistics.Interval = timeBuffer;
else
timerRecalcStatistics.Interval = 1;
stopwatchForRecalcStatistics.Reset();
timerRecalcStatistics.Enabled = true;
}
}
ad 1) It is not important whether ThreadPool can defer execution of callback method, because anyway callback is not guaranteed to complete execution before another timer interval(s) elapses (thread can be suspended by thread scheduler for example, or callback might call long-running function).
ad 2) This is what MSDN says about Timer class:
If the SynchronizingObject property is null, the Elapsed event is
raised on a ThreadPool thread. If processing of the Elapsed event
lasts longer than Interval, the event might be raised again on another
ThreadPool thread. In this situation, the event handler should be
reentrant.
So the answer is YES, callback can be executing on multiple threads concurrently.
ad 3) YES. And you should either avoid using shared resources (timerRecalcStatistics, stopwatchForRecalcStatistics) in callback method, or synchronize access to these shared resources (for example with lock), or set appropriate object to Timer's SynchronizingObject property, or set AutoReset property of Timer to false (and enable timer again at the end of timer callback).
UPDATE:
I thing that Jon Skeet's answer doesn't solve your problem. Also implementing your own SynchonizingObject is IMHO more complicated than necessary (but it's hard to say without knowing whole problem). I hope this implementation should work (but I didn't tested it):
public class MySynchronizeInvoke : ISynchronizeInvoke
{
private object SyncObject = new Object();
private delegate object InvokeDelegate(Delegate method, object[] args);
public IAsyncResult BeginInvoke(Delegate method, object[] args)
{
ElapsedEventHandler handler = (ElapsedEventHandler)method;
InvokeDelegate D = Invoke;
return D.BeginInvoke(handler, args, CallbackMethod, null);
}
private void CallbackMethod(IAsyncResult ar)
{
AsyncResult result = ar as AsyncResult;
if(result != null)
((InvokeDelegate)result.AsyncDelegate).EndInvoke(ar);
}
public object EndInvoke(IAsyncResult result)
{
result.AsyncWaitHandle.WaitOne();
return null;
}
public object Invoke(Delegate method, object[] args)
{
lock(SyncObject)
{
ElapsedEventHandler handler = (ElapsedEventHandler)method;
handler(args[0], (ElapsedEventArgs)args[1]);
return null;
}
}
public bool InvokeRequired
{
get { return true; }
}
}
From the documentation on System.Timers.Timer:
If the SynchronizingObject property is null, the Elapsed event is
raised on a ThreadPool thread. If processing of the Elapsed event
lasts longer than Interval, the event might be raised again on another
ThreadPool thread. In this situation, the event handler should be
reentrant.
So to answer your questions:
Yes, it runs on a threadpool thread, and is subject to threadpool filling up and deferring like anything else. Given that the threadpool now has a maximum of hundreds of threads, this shouldn't be an issue. If it is, you have bigger problems.
Assuming that you do not set a synchronizing object or otherwise sychronize your callback, yes, multiple callback can overlap. If you give the timer a synchronizing object, it will not 'overlap' events.
The code that you provided does not synchronize it's callback in any way, and so yes it can have multiple overlapping, simultaneously executing copies of your callback. You should synchronize the method using something like a lock statement if you want all of the instances of the class to be synchronized one another, or use the SynchronizingObject of the timer if you want each individual instance of the class to only ever have one callback running at any given time.

C# threading pattern that will let me flush

I have a class that implements the Begin/End Invocation pattern where I initially used ThreadPool.QueueUserWorkItem() to thread my work. The work done on the thread doesn't loop but does takes a bit of time to process so the work itself is not easily stopped.
I now have the side effect where someone using my class is calling the Begin (with callback) a ton of times to do a lot of processing so ThreadPool.QueueUserWorkItem is creating a ton of threads to do the processing. That in itself isn't bad but there are instances where they want to abandon the processing and start a new process but they are forced to wait for their first request to finish.
Since ThreadPool.QueueUseWorkItem() doesn't allow me to cancel the threads I am trying to come up with a better way to queue up the work and maybe use an explicit FlushQueue() method in my class to allow the caller to abandon work in my queue.
Anyone have any suggestion on a threading pattern that fits my needs?
Edit: I'm currently targeting the 2.0 framework. I'm currently thinking that a Consumer/Producer queue might work. Does anyone have thoughts on the idea of flushing the queue?
Edit 2 Problem Clarification:
Since I'm using the Begin/End pattern in my class every time the caller uses the Begin with callback I create a whole new thread on the thread pool. This call does a very small amount of processing and is not where I want to cancel. It's the uncompleted jobs in the queue I wish to stop.
The fact that the ThreadPool will create 250 threads per processor by default means if you ask the ThreadPool to queue a large amount of items with QueueUserWorkItem() you end up creating a huge amount of concurrent threads that you have no way of stopping.
The caller is able to push the CPU to 100% with not only the work but the creation of the work because of the way I queued the threads.
I was thinking by using the Producer/Consumer pattern I could queue these threads into my own queue that would allow me to moderate how many threads I create to avoid the CPU spike creating all the concurrent threads. And that I might be able to allow the caller of my class to flush all the jobs in the queue when they are abandoning the requests.
I am currently trying to implement this myself but figured SO was a good place to have someone say look at this code or you won't be able to flush because of this or flushing isn't the right term you mean this.
EDIT My answer does not apply since OP is using 2.0. Leaving up and switching to CW for anyone who reads this question and using 4.0
If you are using C# 4.0, or can take a depedency on one of the earlier version of the parallel frameworks, you can use their built-in cancellation support. It's not as easy as cancelling a thread but the framework is much more reliable (cancelling a thread is very attractive but also very dangerous).
Reed did an excellent article on this you should take a look at
http://reedcopsey.com/2010/02/17/parallelism-in-net-part-10-cancellation-in-plinq-and-the-parallel-class/
A method I've used in the past, though it's certainly not a best practice is to dedicate a class instance to each thread, and have an abort flag on the class. Then create a ThrowIfAborting method on the class that is called periodically from the thread (particularly if the thread's running a loop, just call it every iteration). If the flag has been set, ThrowIfAborting will simply throw an exception, which is caught in the main method for the thread. Just make sure to clean up your resources as you're aborting.
You could extend the Begin/End pattern to become the Begin/Cancel/End pattern. The Cancel method could set a cancel flag that the worker thread polls periodically. When the worker thread detects a cancel request, it can stop its work, clean-up resources as needed, and report that the operation was canceled as part of the End arguments.
I've solved what I believe to be your exact problem by using a wrapper class around 1+ BackgroundWorker instances.
Unfortunately, I'm not able to post my entire class, but here's the basic concept along with it's limitations.
Usage:
You simply create an instance and call RunOrReplace(...) when you want to cancel your old worker and start a new one. If the old worker was busy, it is asked to cancel and then another worker is used to immediately execute your request.
public class BackgroundWorkerReplaceable : IDisposable
{
BackgroupWorker activeWorker = null;
object activeWorkerSyncRoot = new object();
List<BackgroupWorker> workerPool = new List<BackgroupWorker>();
DoWorkEventHandler doWork;
RunWorkerCompletedEventHandler runWorkerCompleted;
public bool IsBusy
{
get { return activeWorker != null ? activeWorker.IsBusy; : false }
}
public BackgroundWorkerReplaceable(DoWorkEventHandler doWork, RunWorkerCompletedEventHandler runWorkerCompleted)
{
this.doWork = doWork;
this.runWorkerCompleted = runWorkerCompleted;
ResetActiveWorker();
}
public void RunOrReplace(Object param, ...) // Overloads could include ProgressChangedEventHandler and other stuff
{
try
{
lock(activeWorkerSyncRoot)
{
if(activeWorker.IsBusy)
{
ResetActiveWorker();
}
// This works because if IsBusy was false above, there is no way for it to become true without another thread obtaining a lock
if(!activeWorker.IsBusy)
{
// Optionally handle ProgressChangedEventHandler and other features (under the lock!)
// Work on this new param
activeWorker.RunWorkerAsync(param);
}
else
{ // This should never happen since we create new workers when there's none available!
throw new LogicException(...); // assert or similar
}
}
}
catch(...) // InvalidOperationException and Exception
{ // In my experience, it's safe to just show the user an error and ignore these, but that's going to depend on what you use this for and where you want the exception handling to be
}
}
public void Cancel()
{
ResetActiveWorker();
}
public void Dispose()
{ // You should implement a proper Dispose/Finalizer pattern
if(activeWorker != null)
{
activeWorker.CancelAsync();
}
foreach(BackgroundWorker worker in workerPool)
{
worker.CancelAsync();
worker.Dispose();
// perhaps use a for loop instead so you can set worker to null? This might help the GC, but it's probably not needed
}
}
void ResetActiveWorker()
{
lock(activeWorkerSyncRoot)
{
if(activeWorker == null)
{
activeWorker = GetAvailableWorker();
}
else if(activeWorker.IsBusy)
{ // Current worker is busy - issue a cancel and set another active worker
activeWorker.CancelAsync(); // Make sure WorkerSupportsCancellation must be set to true [Link9372]
// Optionally handle ProgressEventHandler -=
activeWorker = GetAvailableWorker(); // Ensure that the activeWorker is available
}
//else - do nothing, activeWorker is already ready for work!
}
}
BackgroupdWorker GetAvailableWorker()
{
// Loop through workerPool and return a worker if IsBusy is false
// if the loop exits without returning...
if(activeWorker != null)
{
workerPool.Add(activeWorker); // Save the old worker for possible future use
}
return GenerateNewWorker();
}
BackgroundWorker GenerateNewWorker()
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true; // [Link9372]
//worker.WorkerReportsProgress
worker.DoWork += doWork;
worker.RunWorkerCompleted += runWorkerCompleted;
// Other stuff
return worker;
}
} // class
Pro/Con:
This has the benefit of having a very low delay in starting your new execution, since new threads don't have to wait for old ones to finish.
This comes at the cost of a theoretical never-ending growth of BackgroundWorker objects that never get GC'd. However, in practice the code below attempts to recycle old workers so you shouldn't normally encounter a large pool of ideal threads. If you are worried about this because of how you plan to use this class, you could implement a Timer which fires a CleanUpExcessWorkers(...) method, or have ResetActiveWorker() do this cleanup (at the cost of a longer RunOrReplace(...) delay).
The main cost from using this is precisely why it's beneficial - it doesn't wait for the previous thread to exit, so for example, if DoWork is performing a database call and you execute RunOrReplace(...) 10 times in rapid succession, the database call might not be immediately canceled when the thread is - so you'll have 10 queries running, making all of them slow! This generally tends to work fine with Oracle, causing only minor delays, but I do not have experiences with other databases (to speed up the cleanup, I have the canceled worker tell Oracle to cancel the command). Proper use of the EventArgs described below mostly solves this.
Another minor cost is that whatever code this BackgroundWorker is performing must be compatible with this concept - it must be able to safely recover from being canceled. The DoWorkEventArgs and RunWorkerCompletedEventArgs have a Cancel/Cancelled property which you should use. For example, if you do Database calls in the DoWork method (mainly what I use this class for), you need to make sure you periodically check these properties and take perform the appropriate clean-up.

Is it possible to put an event handler on a different thread to the caller?

Lets say I have a component called Tasking (that I cannot modify) which exposes a method “DoTask” that does some possibly lengthy calculations and returns the result in via an event TaskCompleted. Normally this is called in a windows form that the user closes after she gets the results.
In my particular scenario I need to associate some data (a database record) with the data returned in TaskCompleted and use that to update the database record.
I’ve investigated the use of AutoResetEvent to notify when the event is handled. The problem with that is AutoResetEvent.WaitOne() will block and the event handler will never get called. Normally AutoResetEvents is called be a separate thread, so I guess that means that the event handler is on the same thread as the method that calls.
Essentially I want to turn an asynchronous call, where the results are returned via an event, into a synchronous call (ie call DoSyncTask from another class) by blocking until the event is handled and the results placed in a location accessible to both the event handler and the method that called the method that started the async call.
public class SyncTask
{
TaskCompletedEventArgs data;
AutoResetEvent taskDone;
public SyncTask()
{
taskDone = new AutoResetEvent(false);
}
public string DoSyncTask(int latitude, int longitude)
{
Task t = new Task();
t.Completed = new TaskCompletedEventHandler(TaskCompleted);
t.DoTask(latitude, longitude);
taskDone.WaitOne(); // but something more like Application.DoEvents(); in WinForms.
taskDone.Reset();
return data.Street;
}
private void TaskCompleted(object sender, TaskCompletedEventArgs e)
{
data = e;
taskDone.Set(); //or some other mechanism to signal to DoSyncTask that the work is complete.
}
}
In a Windows App the following works correctly.
public class SyncTask
{
TaskCompletedEventArgs data;
public SyncTask()
{
taskDone = new AutoResetEvent(false);
}
public string DoSyncTask(int latitude, int longitude)
{
Task t = new Task();
t.Completed = new TaskCompletedEventHandler(TaskCompleted);
t.DoTask(latitude, longitude);
while (data == null) Application.DoEvents();
return data.Street;
}
private void TaskCompleted(object sender, TaskCompletedEventArgs e)
{
data = e;
}
}
I just need to replicate that behaviour in a window service, where Application.Run isn’t called and the ApplicationContext object isn’t available.
I've had some trouble lately with making asynchronous calls and events at threads and returning them to the main thread.
I used SynchronizationContext to keep track of things. The (pseudo)code below shows what is working for me at the moment.
SynchronizationContext context;
void start()
{
//First store the current context
//to call back to it later
context = SynchronizationContext.Current;
//Start a thread and make it call
//the async method, for example:
Proxy.BeginCodeLookup(aVariable,
new AsyncCallback(LookupResult),
AsyncState);
//Now continue with what you were doing
//and let the lookup finish
}
void LookupResult(IAsyncResult result)
{
//when the async function is finished
//this method is called. It's on
//the same thread as the the caller,
//BeginCodeLookup in this case.
result.AsyncWaitHandle.WaitOne();
var LookupResult= Proxy.EndCodeLookup(result);
//The SynchronizationContext.Send method
//performs a callback to the thread of the
//context, in this case the main thread
context.Send(new SendOrPostCallback(OnLookupCompleted),
result.AsyncState);
}
void OnLookupCompleted(object state)
{
//now this code will be executed on the
//main thread.
}
I hope this helps, as it fixed the problem for me.
Maybe you could get DoSyncTask to start a timer object that checks for the value of your data variable at some appropriate interval. Once data has a value, you could then have another event fire to tell you that data now has a value (and shut the timer off of course).
Pretty ugly hack, but it could work... in theory.
Sorry, that's the best I can come up with half asleep. Time for bed...
I worked out a solution to the async to sync problem, at least using all .NET classes.
Link
It still doesn't work with COM. I suspect because of STA threading. The Event raised by the .NET component that hosts the COM OCX is never handled by my worker thread, so I get a deadlock on WaitOne().
someone else may appreciate the solution though :)
If Task is a WinForms component, it might be very aware of threading issues and Invoke the event handler on the main thread -- which seems to be what you're seeing.
So, it might be that it relies on a message pump happening or something. Application.Run has overloads that are for non-GUI apps. You might consider getting a thread to startup and pump to see if that fixes the issue.
I'd also recommend using Reflector to get a look at the source code of the component to figure out what it's doing.
You've almost got it. You need the DoTask method to run on a different thread so the WaitOne call won't prevent work from being done. Something like this:
Action<int, int> doTaskAction = t.DoTask;
doTaskAction.BeginInvoke(latitude, longitude, cb => doTaskAction.EndInvoke(cb), null);
taskDone.WaitOne();
My comment on Scott W's answer seems a little cryptic after I re-read it. So let me be more explicit:
while( !done )
{
taskDone.WaitOne( 200 );
Application.DoEvents();
}
The WaitOne( 200 ) will cause it to return control to your UI thread 5 times per second (you can adjust this as you wish). The DoEvents() call will flush the windows event queue (the one that handles all windows event handling like painting, etc.). Add two members to your class (one bool flag "done" in this example, and one return data "street" in your example).
That is the simplest way to get what you want done. (I have very similar code in an app of my own, so I know it works)
Your code is almost right... I just changed
t.DoTask(latitude, longitude);
for
new Thread(() => t.DoTask(latitude, longitude)).Start();
TaskCompleted will be executed in the same thread as DoTask does. This should work.

Categories

Resources