Why does working with two ManualResetEvents cause a deadlock here? - c#

I'm performing an async operation for an upload using Starksoft.Net.Ftp.
Looks like that:
public void UploadFile(string filePath, string packageVersion)
{
_uploadFtpClient= new FtpClient(Host, Port, FtpSecurityProtocol.None)
{
DataTransferMode = UsePassiveMode ? TransferMode.Passive : TransferMode.Active,
FileTransferType = TransferType.Binary,
};
_uploadFtpClient.TransferProgress += TransferProgressChangedEventHandler;
_uploadFtpClient.PutFileAsyncCompleted += UploadFinished;
_uploadFtpClient.Open(Username, Password);
_uploadFtpClient.ChangeDirectoryMultiPath(Directory);
_uploadFtpClient.MakeDirectory(newDirectory);
_uploadFtpClient.ChangeDirectory(newDirectory);
_uploadFtpClient.PutFileAsync(filePath, FileAction.Create);
_uploadResetEvent.WaitOne();
_uploadFtpClient.Close();
}
private void UploadFinished(object sender, PutFileAsyncCompletedEventArgs e)
{
if (e.Error != null)
{
if (e.Error.InnerException != null)
UploadException = e.Error.InnerException;
}
_uploadResetEvent.Set();
}
As you can see, there is a ManualResetEvent in there, which is declared as private variable on top of the class:
private ManualResetEvent _uploadResetEvent = new ManualResetEvent(false);
Well, the sense is just that it should wait for the upload to complete, but it must be async for reporting progress, that's all.
Now, this just works fine.
I have a second method that should cancel the upload, if wished.
public void Cancel()
{
_uploadFtpClient.CancelAsync();
}
When the upload is cancelled a directory on the server also must be deleted.
I have a method for this, too:
public void DeleteDirectory(string directoryName)
{
_uploadResetEvent.Set(); // As the finished event of the upload is not called when cancelling, I need to set the ResetEvent manually here.
if (!_hasAlreadyFixedStrings)
FixProperties();
var directoryEmptyingClient = new FtpClient(Host, Port, FtpSecurityProtocol.None)
{
DataTransferMode = UsePassiveMode ? TransferMode.Passive : TransferMode.Active,
FileTransferType = TransferType.Binary
};
directoryEmptyingClient.Open(Username, Password);
directoryEmptyingClient.ChangeDirectoryMultiPath(String.Format("/{0}/{1}", Directory, directoryName));
directoryEmptyingClient.GetDirListAsyncCompleted += DirectoryListingFinished;
directoryEmptyingClient.GetDirListAsync();
_directoryFilesListingResetEvent.WaitOne(); // Deadlock appears here
if (_directoryCollection != null)
{
foreach (FtpItem directoryItem in _directoryCollection)
{
directoryEmptyingClient.DeleteFile(directoryItem.Name);
}
}
directoryEmptyingClient.Close();
var directoryDeletingClient = new FtpClient(Host, Port, FtpSecurityProtocol.None)
{
DataTransferMode = UsePassiveMode ? TransferMode.Passive : TransferMode.Active,
FileTransferType = TransferType.Binary
};
directoryDeletingClient.Open(Username, Password);
directoryDeletingClient.ChangeDirectoryMultiPath(Directory);
directoryDeletingClient.DeleteDirectory(directoryName);
directoryDeletingClient.Close();
}
private void DirectoryListingFinished(object sender, GetDirListAsyncCompletedEventArgs e)
{
_directoryCollection = e.DirectoryListingResult;
_directoryFilesListingResetEvent.Set();
}
As the finished event of the upload is not called when cancelling, I need to set the ResetEvent manually in the DeleteDirectory-method.
Now, what am I doing here: I first list all files in the directory in order to delete them, as a filled folder can't be deleted.
This method GetDirListAsync is also async which means I need another ManualResetEvent as I don't want the form to freeze.
This ResetEvent is _directoryFilesListingResetEvent. It is declared like the _uploadResetEvent above.
Now, the problem is, it goes to the WaitOne-call of the _directoryFilesListingResetEvent and then it stucks. A deadlock appears and the form freezes. (I've also marked it in the code)
Why is that?
I tried to move the call of _uploadResetEvent.Set(), but it doesn't change.
Does anyone see the problem?
When I try to call the DeleteDirectory-method alone without any upload, it works as well.
I think the problem is that both ResetEvents use the same resource or something and overlap themselves, I don't know.
Thanks for your help.

You are not using this library correctly. The MREs you added cause deadlock. That started with _uploadResetEvent.WaitOne(), blocking the UI thread. This is normally illegal, the CLR ensures that your UI does not go completely dead by pumping a message loop itself. That makes it look like it is still alive, it still repaints for example. A rough equivalent of DoEvents(), although not nearly as dangerous.
But the biggest problem with it is that it will not allow your PutFileAsyncCompleted event handler to run, the underlying async worker is a plain BackgroundWorker. It fires its events on the same thread that started it, which is very nice. But it cannot call its RunWorkerCompleted event handler until the UI thread goes idle. Which is not nice, the thread is stuck in the WaitOne() call. Exact same story for what you are debugging now, your GetDirListAsyncCompleted event handler cannot run for the same reason. So it just freezes there without being able to make progress.
So eliminate _uploadResetEvent completely, rely on your UploadFinished() method instead. You can find out if it was canceled from the e.Cancelled property. Only then do you start the code to delete the directory. Follow the same pattern, using the corresponding XxxAsyncCompleted event to decide what to do next. No need for MREs at all.

Looking at the source, it appears FtpClient uses a BackgroundWorker to perform asynchronous operations. That means its completion event will be posted to whatever SynchronizationContext was set at the time the worker was created. I'll bet the completion of CancelAsync pushes you back onto the UI thread, which blocks when you call WaitOne on the directory list reset event. The GetDirListAsyncCompleted event gets posted to UI message loop, but since the UI thread is blocked, it will never run, and the reset event will never be set.
BOOM! Deadlock.

Related

Creating an async resource watcher in c# (service broker queue resource)

Partly as an exercise in exploring async, I though I'd try creating a ServiceBrokerWatcher class. The idea is much the same as a FileSystemWatcher - watch a resource and raise an event when something happens. I was hoping to do this with async rather than actually creating a thread, because the nature of the beast means that most of the time it is just waiting on a SQL waitfor (receive ...) statement. This seemed like an ideal use of async.
I have written code which "works", in that when I send a message through broker, the class notices it and fires off the appropriate event. I thought this was super neat.
But I suspect I have gotten something fundamentally wrong somewhere in my understanding of what is going on, because when I try to stop the watcher it doesn't behave as I expect.
First a brief overview of the components, and then the actual code:
I have a stored procedure which issues a waitfor (receive...) and returns a result set to the client when a message is received.
There is a Dictionary<string, EventHandler> which maps message type names (in the result set) to the appropriate event handler. For simplicity I only have the one message type in the example.
The watcher class has an async method which loops "forever" (until cancellation is requested), which contains the execution of the procedure and the raising of the events.
So, what's the problem? Well, I tried hosting my class in a simple winforms application, and when I hit a button to call the StopListening() method (see below), execution isn't cancelled right away as I thought it would be. The line listener?.Wait(10000) will in fact wait for 10 seconds (or however long I set the timeout). If I watch what happens with SQL profiler I can see that the attention event is being sent "straight away", but still the function does not exit.
I have added comments to the code starting with "!" where I suspect I have misunderstood something.
So, main question: Why isn't my ListenAsync method "honoring" my cancellation request?
Additionally, am I right in thinking that this program is (most of the time) consuming only one thread? Have I done anything dangerous?
Code follows, I tried to cut it down as much as I could:
// class members //////////////////////
private readonly SqlConnection sqlConnection;
private CancellationTokenSource cts;
private readonly CancellationToken ct;
private Task listener;
private readonly Dictionary<string, EventHandler> map;
public void StartListening()
{
if (listener == null)
{
cts = new CancellationTokenSource();
ct = cts.Token;
// !I suspect assigning the result of the method to a Task is wrong somehow...
listener = ListenAsync(ct);
}
}
public void StopListening()
{
try
{
cts.Cancel();
listener?.Wait(10000); // !waits the whole 10 seconds for some reason
} catch (Exception) {
// trap the exception sql will raise when execution is cancelled
} finally
{
listener = null;
}
}
private async Task ListenAsync(CancellationToken ct)
{
using (SqlCommand cmd = new SqlCommand("events.dequeue_target", sqlConnection))
using (CancellationTokenRegistration ctr = ct.Register(cmd.Cancel)) // !necessary?
{
cmd.CommandTimeout = 0;
while (!ct.IsCancellationRequested)
{
var events = new List<string>();
using (var rdr = await cmd.ExecuteReaderAsync(ct))
{
while (rdr.Read())
{
events.Add(rdr.GetString(rdr.GetOrdinal("message_type_name")));
}
}
foreach (var handler in events.Join(map, e => e, m => m.Key, (e, m) => m.Value))
{
if (handler != null && !ct.IsCancellationRequested)
{
handler(this, null);
}
}
}
}
}
You don't show how you've bound it to the WinForms app, but if you are using regular void button1click methods, you may be running into this issue.
So your code will run fine in a console app (it does when I try it) but deadlock when called via the UI thread.
I'd suggest changing your controller class to expose async start and stop methods, and call them via e.g.:
private async void btStart_Click(object sender, EventArgs e)
{
await controller.StartListeningAsync();
}
private async void btStop_Click(object sender, EventArgs e)
{
await controller.StopListeningAsync();
}
Peter had the right answer. I was confused for several minutes about what was deadlocking, but then I had my forehead slapping moment. It is the continuation of ListenAsync after the ExecuteReaderAsync is cancelled, because it's just a task, not a thread of its own. That was, after all, the whole point!
Then I wondered... OK, what if I tell the async part of ListenAsync() that it doesn't need the UI thread. I will call ExecuteReaderAsync(ct) with .ConfigureAwait(false)! Aha! Now the class methods don't have to be async anymore, because in StopListening() I can just listener.Wait(10000), the wait will continue the task internally on a different thread, and the consumer is none the wiser. Oh boy, so clever.
But no, I can't do that. Not in a webforms application at least. If I do that then the textbox is not updated. And the reason for that seems clear enough: the guts of ListenAsync invoke an event handler, and that event handler is a function which wants to update text in a textbox - which no doubt has to happen on the UI thread. So it doesn't deadlock, but it also can't update the UI. If I set a breakpoint in the handler which wants to update the UI the line of code is hit, but the UI can't be changed.
So in the end it seems the only solution in this case is indeed to "go async all the way down". Or in this case, up!
I was hoping that I didn't have to do that. The fact that the internals of my Watcher are using async methodologies rather than just spawning a thread is, in my mind, an "implementation detail" that the caller shouldn't have to care about. But a FileSystemWatcher has exactly the same issue (the need to control.Invoke if you want to update a GUI based on a watcher event), so that's not so bad. If I was a consumer that had to choose between using async or using Invoke, I'd choose async!

Which is the better practice: Multiple BackgroundWorkers or Change the DoWork to a different method? [duplicate]

Suppose you have a search textbox and have a search algorithm attached to the TextChanged event, that runs with a BackgroundWorker. If there comes a new character in the textbox, i need to cancel the previous search and run it again.
I tried using events in between the main thread and the bgw, from this previous question, but I still get the error "currently busy and cannot run multiple tasks concurrently"
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
private void txtSearch_TextChanged(object sender, EventArgs e)
{
SearchWithBgw();
}
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
void bgw_Search_DoWork(object sender, DoWorkEventArgs e)
{
Search(txtSearch.Text, e);
}
private void Search(string aQuery, DoWorkEventArgs e)
{
int i = 1;
while (i < 3) // simulating search processing...
{
Thread.Sleep(1000);
i++;
if (bgw_Search.CancellationPending)
{
_resetEvent.Set(); // signal that worker is done
e.Cancel = true;
return;
}
}
}
EDIT To reflect answers. Don´t reuse the BackgroundWorker, create a new one:
private void SearchWithBgw()
{
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
bgw_Search = new BackgroundWorker();
bgw_Search.WorkerSupportsCancellation = true;
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
bgw_Search.RunWorkerAsync();
}
When the _resetEvent.WaitOne() call completes, the worker thread isn't actually done. It is busy returning from DoWork() and waiting for an opportunity to run the RunWorkerCompleted event, if any. That takes time.
There is no reliable way to ensure the BGW is completed in a synchronous way. Blocking on IsBusy or waiting for the RunWorkerCompleted event to run is going to cause deadlock. If you really want to use only one bgw then you'll have to queue the requests. Or just don't sweat the small stuff and allocate another bgw. They cost very little.
Create a new background worker if the old one exists.
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
Also I know you put fake code in, but you want to make sure you set _resetEvent when the code completes normally too.
Do not reuse a Backgroundworker. It is a cheap resource, it is not a Thread.
make sure your Bgw code stops, yours looks OK. The Bgw will release the Thread to the pool.
but in the mean time, create a new Task/Bgw for a new job.
You may want to unsubscribe your Completed event from the old Bgw.
I think you should consider not cancelling the background worker.
If you cancel requests and the user types faster than your server returns queries, he will not see suggestions until he is finished typing.
In interactive scenarios like this, It could be better to show responses that run behind with what the user's typing. Your user will know he can stop typing if the word he has in mind is your suggestions list.
This will be also better for your server when it is busy, because instead of many cancelled requests, who will cost something but that are ultimately not shown, there will be fewer requests whose response you actually use.
I ran into similar issues with (3d) rendering applications, where the beginner's mistake is to cancel and rerender on every mousemove. This lead to a lot of computation and little interactive feedback.

Thread.Join in UI thread also blocking child thread

This may well be a dumb question and if this has already been answered elsewhere then I'd really appreciate it if someone could point me to it as my searching hasn't turned up anything definitive.
In a nutshell, my problem is that when I do childThread.Join() in the UI thread on a child thread which has been flagged to stop the childThread seems to block as well as the main thread so everything just hangs.
That the UI will block due to using Join is not a problem in and of itself at the moment since the childThread should finish in under a second after it's told to quit anyway.
This happens while I'm waiting for a thread running a repeating process to quit before I can run another method which returns some information but can't be run at the same time as the other process.
My Winforms application is integrating with a piece of usb hardware by pinvoking the C API for the hardware.
The hardware API has a method that will start off a process that will run indefinitely and repeatedly and rapidly callback with new information which I then need to pass to the UI.
This operation can be cancelled by another call to the hardware API which sets a flag the hardware can see so it knows to quit.
I've wrapped this C API with my own C# code, and within the wrapper I've had to spin out the start process call in another thread so that the activity doesn't block the UI.
Here are the edited highlights of roughly what I'm doing.
public class DeviceWrapper
{
Thread childThread = null;
void DeviceWrapper
{
//Set the callback to be used by the StartGettingInformation() process
PInvokeMethods.SetGetInformationCallback(InformationAcquiredCallback);
}
public void StartProcess()
{
childThread = new Thread(new ThreadStart(GetInformationProcess))
childThread.Start();
}
void GetInformationProcess()
{
PInvokeMethods.StartGettingInformation();
}
//This callback occurs inside the childThread
void InformationAcquiredCallback(Status status, IntPtr information)
{
//This callback is triggered when anything happens in the
//StartGettingInformation() method, such as when the information
//is ready to be retrieved, or when the process has been cancelled.
if(status == Status.InformationAcquired)
{
FireUpdateUIEvent();
}
//If the cancel flag has been set to true this will be hit.
else if(status == Status.Cancelled)
{
//Reset the cancel flag so the next operation works ok
PInvokeMethods.SetCancelFlag(false);
childThread.Abort();
}
}
//This method runs once, and can't run at the same time as GetInformationProcess
public string GetSpecificInformation()
{
//This triggers InformationAcquiredCallback with a status of Cancelled
StopProcess();
if(childThread.IsAlive)
{
childThread.Join();
}
return PInvokeMethods.GetSpecificInformation();
}
public void StopProcess()
{
PInvokeMethods.SetCancelFlag(true);
}
}
Using this code when I call childThread.Join() the whole application grinds to a halt (which I'd expect for the UI and that's fine) and the childThread also seems to halt because the callback never gets hit again.
However, if I use the following code instead:
public string GetSpecificInformation()
{
//This triggers InformationAcquiredCallback with a status of Cancelled
StopProcess();
string s = "";
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
{
if(childThread.IsAlive)
{
childThread.Join();
}
s = PInvokeMethods.GetSpecificInformation();
}));
return s;
}
Then everything gets hit as expected and childThread does finish and all is well, except obviously my string gets returned empty before the WaitCallback fires and assigns to it.
So, do I just have to suck it up and change the class so that I use the QueueUserWorkItem and WaitCallback and fire an event to deal with my string return?
Is there something daft I'm doing in my first approach that's causing the childThread to block as well?
Or is there another tactic or class entirely that I should be using, bearing in mind it's .NET 3.5 I'm on?
Well, FireUpdateUIEvent(); sounds like a method that might Post Send to the MsgQueue (Control.Invoke()). When the main thread is waiting in a Join() then you have a classic deadlock.
In Addition, childThread.Abort() is not considered safe.
So, do I just have to suck it up and change the class so that I use the QueueUserWorkItem and WaitCallback and fire an event to deal with my string return?
I certainly would re-design it. It probably can be simplified a bit.

Synchronizing Events

I noticed that sometimes my code becomes out of sync if an event fires too quickly. I was wondering if there was a better approach. Under a normal scenario the DeviceOpenedEvent fires after I tell the thread to WaitOne in the TestDevice method, but I have seen in some cases where the event gets fired before the thread has a chance to wait.
protected AutoResetEvent TestAutoResetEvent = new AutoResetEvent(false);
public EventEnum WaitForEvent = EventEnum.None;
bool TestDevice()
{
OpenDevice();
WaitForEvent = EventEnum.DeviceOpened;
TestAutoResetEvent.WaitOne();
WaitForEvent = EventEnum.NoWait;
//Continue with other tests
}
void DeviceOpenedEvent()
{
if (WaitForEvent == EventEnum.DeviceOpened)
TestAutoResetEvent.Set();
}
Under normal circumstances it looks like this:
Open Device
WaitOne()
DeviceOpenedEvent occurs
Set()
This is what I'm seeing my logs sometimes:
Open Device
DeviceOpenedEvent occurs
WaitOne() Essentially stuck here forever
Since OpenDevice is asynchronous (as you mentioned in a comment), it runs in a different thread than its caller. Sometimes it will finish before the next line in source executes:
OpenDevice(); // Async: may finish before the next line executes!
WaitForEvent = EventEnum.DeviceOpened;
When that happens DeviceOpenedEvent doesn't do what you want it to, because WaitForEvent is still EventEnum.None:
if (WaitForEvent == EventEnum.DeviceOpened)
TestAutoResetEvent.Set();
The solution is to change your code so that you signal completion inside a method that's guaranteed to run in the correct order. Here's a simple implementation that removes the enumeration and uses a single wait handle for each event you need to wait on:
protected AutoResetEvent deviceOpenedEvent = new AutoResetEvent(false);
protected AutoResetEvent deviceLockedEvent = new AutoResetEvent(false);
bool TestDevice() {
OpenDevice();
// Do some unrelated parallel stuff here ... then
deviceOpenedEvent.WaitOne();
LockDevice();
deviceLockedEvent.WaitOne();
}
void DeviceOpenedEvent() {
deviceOpenedEvent.Set();
}
It's even easier if you control OpenDevice: just call deviceOpened.Set() when it's done. You could even change OpenDevice to accept the auto reset event and construct it right inside TestDevice, which would reduce your exposure to multithreading bugs.
This should not be a problem. The documentation for AutoResetEvent states:
If a thread calls WaitOne while the
AutoResetEvent is in the signaled
state, the thread does not block.
The following code does not cause WaitOne to block, for instance:
AutoResetEvent waitHandle = new AutoResetEvent(false);
waitHandle.Set();
waitHandle.WaitOne();
Console.WriteLine("After WaitOne");

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