Need explanation of win API "Asynchronous Procedure Calls" relative to .NET
I have a bug. In WinForm .NET2.0 application my HeavyFunction can be called from mouse click event handlers. So, When I'm starting fast clicking I have crash on file writing because of HeavyFunction entered second time.
My solution. I remembered the things relative to APC and decided to synchronize the things fairly. I.e. I want to exclude APC effects in the HeavyFunction. So, I used ThreadPool. For synchronization I tried to use SyncLock (Visual Basic). And failed. Mutexes don't work either. I simply cannot understand how my HeavyFunction can be called from the same thread twice at a time. But it does. I thought that if I use ThreadPool then I force every call be executed on different thread.
The definition of APC http://msdn.microsoft.com/en-us/library/ms681951%28v=vs.85%29.aspx.
Here is "Thread Synchronization Fairness in the CLR" by Jeffrey Richter (makes me sick): http://codeguru.earthweb.com/csharp/.net/net_general/threads/article.php/c4647/Thread-Synchronization-Fairness-in-the-CLR.htmEDIT: It seems that I found the reason of crash exception. It's antivirus AVG. Probably he considers suspicious activity when somebody clicks very fast and it causes fast file open/write operations. Or it slows down my PC so much. Anyway I cannot reproduce crash when AVG disabled. Although ProcMon shows that AVG access the file while enabled.
Finally, can somebody explain APC to me? Namely, can APC lead to double enter to the same function in the same thread? I understood that it can from this text:
"An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread, the system issues a software interrupt. The next time the thread is sched开发者_如何转开发uled, it will run the APC function. "I'm afraid to die without this understanding.
It is not clear to me whether you want many calls at the same time... you could just disabled the button while the heavyFunction executes?
EDIT
Since it seems you need every call handled, you should uses a Queue that stores every call, and a thread that executes them one by one.
class yourform {
Queue<somedata> queue = new Queue<somedata>();
Thread caller;
private bool Closing {get;set;}
public yourform() {
/// initcomponents bla bla
Thread t = new Thread((ThreadStart)delegate() {
while (!Closing) {
lock (queue) {
if (queue.Peek()) {
somedata data = queue.Dequeue();
HeavyFunction(data); // add invoke if required
}
}
}
}).Start();
caller = t;
}
~yourform() {
Closing = true;
caller.Join();
}
void clickEventHandler(object sender, EventArgs e) {
somedata data = new somedata();
queue.Queue(data);
}
}
Edit: you did not tag Visual basic so I did not notice , but you can use an online vb to c# translator...
You're seeing this behavior because Win32 messages are implicitly reentrant. This is particularly true in .NET because the runtime may pump on your behalf whenever your UI thread blocks.
The normal way to work around this behavior is to either disable the controls while the long-running event handler is executing, or keep a queue of actions to run instead of kicking off another handler each time.
APCs are a red herring. They can "borrow" a thread in an alertable wait (which the .NET runtime usually uses for blocked UI threads), but an APC will not invoke a UI event handler (at least, I cannot think of any situation that would cause this).
From your comment you say you want the events to be processed one by one in the order they come in. This sounds like a queue. Have a look at this question: How to reuse threads in .NET 3.5 The accepted answer shows a worker queue implementation. If you use it with workerCount = 1
then you have a queue being processed by one thread and the events will be processed in the order they enter the queue.
Your HeavyFunction cannot be called twice on the same thread at the same time. A single thread can only perform one function at a time -- it has to return before it is called again in the same thread.
I'm not sure what is going on, but if your mouse click event handler is creating a new object (with HeavyFunction), then a lock won't help you.
Does HeavyFunction access the same file each time? If so, then a thread pool won't work either because all the threads will still be trying to access a single file.
I think the best solution is to use a queue like ChrisWise suggests.
精彩评论