Invoke method causing dead lock ....

L

LamSoft

It seems that my program is dead lock after running this sentence (bold)

private delegate void updateBuildingDetailCallBack(String key, String Value);

private void updateBuildingDetail(String key, String Value)
{
if (tabBuildings.Controls["lblBuild_Resource_" + key].InvokeRequired)
{
// This is not a UI thread, we cannot update the UI directly
updateBuildingDetailCallBack callBack = delegate(String _key, String _Value)
{
tabBuildings.Controls["lblBuild_Resource_" + _key].Text = _Value;
};
tabBuildings.Controls["lblBuild_Resource_" + key].Invoke(callBack, new object[] { key, Value });
}
else
{
tabBuildings.Controls["lblBuild_Resource_" + key].Text = Value;
}
}

When I try to debug the program, if the program first time calling the function "updateBuildingDetail", the objects of tabBuildings.Controls["lblBuild_Resource_" + key] is still need to be invoked while normally for the first time it doesn't

May I know why cause the program hang after the bolded statement?
Thanks
 
M

Marc Gravell

May I know why cause the program hang after the bolded statement?

Well, what causes updateBuildingDetail to get called? which thread?
How do you start this work? A BackgroundWorker? A callback? What?
If the UI thread is still waiting on a background thread to return
(which it shouldn't necessarily), then yes: this will indeed deadlock.
If the UI is available, it should process the item and return.

If you just care that it happens (not that it happens *right now*),
then just change to BeginInvoke. Also, I wouldn't start navigating the
Controls collection, as you aren't on the UI thread... I'd change this
line to simply use the local (this) InvokeRequired/Invoke/BeginInvoke;
i.e.:

if(InvokeRequired) {
BeginInvoke((MethodInvoker) delegate {
tabBuildings.Controls["lblBuild_Resource_" + key].Text = value;
});
} else {
tabBuildings.Controls["lblBuild_Resource_" + key].Text = value;
}

Note also that Invoke/BeginInvoke are safe to call from the UI thread;
if this is generally going to be called on a non-UI thread (due to the
use-case) I might even remove the "if" etc, and simply
Invoke/BeginInvoke it.

Marc
 
L

LamSoft

updateBuildingDetail is called by a child (non-UI) thread, which this child
thread is created by the main thread.

I found that in the child thread, the invokeRequired of
tabBuildings.Controls["lblBuild_Resource_" + key] is already true..
So I think the program do not know where is the original UI Thread....

As that UI Control is controlled by the code, such as lblBuild_Resource_01,
lblBuild_Resource_02, etc...
So I use Controls Method to change all of those label.

Thanks
 
L

LamSoft

Sorry, one more interesting thing


when i use invoke method, the program will be in deadlock..
but if i use begin invoke, the program will has an error at
"Application.Run(new MyForm());" at Program.cs... and here is the error:

System.Reflection.TargetParameterCountException was unhandled
Message="Parameter count mismatch."
Source="mscorlib"
StackTrace:
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj,
BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo
culture, Boolean skipVisibilityChecks)
at System.Delegate.DynamicInvokeImpl(Object[] args)
at
System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry
tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object
obj)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode
code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at
System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at
System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&
m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd,
Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&
msg)
at
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at
System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at TravianAutoer.Program.Main() in D:\My Documents\Visual Studio
2005\Projects\TravianAutoer\TravianAutoer\Program.cs:line 17
at System.AppDomain.nExecuteAssembly(Assembly assembly, String[]
args)
at System.Runtime.Hosting.ManifestRunner.Run(Boolean checkAptModel)
at System.Runtime.Hosting.ManifestRunner.ExecuteAsAssembly()
at
System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext
activationContext, String[] activationCustomData)
at
System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext
activationContext)
at
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssemblyDebugInZone()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()



LamSoft said:
updateBuildingDetail is called by a child (non-UI) thread, which this
child thread is created by the main thread.

I found that in the child thread, the invokeRequired of
tabBuildings.Controls["lblBuild_Resource_" + key] is already true..
So I think the program do not know where is the original UI Thread....

As that UI Control is controlled by the code, such as
lblBuild_Resource_01, lblBuild_Resource_02, etc...
So I use Controls Method to change all of those label.

Thanks
 
M

Marc Gravell

updateBuildingDetail is called by a child (non-UI) thread
And what is the UI thread doing at this point? Has it returned to it's
normal state? The following is typical bad code that will lead to
deadlocks (and is pointless):

SomeUIMethod() {
...spawn some non-UI work...
...wait for the above to finish / set a flag... // BAD
}

You need to exit the SomeUIMethod to let the UI process it's events.
There is also a hacky way to accomplish this, but I don't recommend it
to the extent that I won't even volunteer what it is (everybody knows
the method I mean ;-p).
I found that in the child thread, the invokeRequired of
tabBuildings.Controls["lblBuild_Resource_" + key] is already true..

When asking from a non-UI thread, we *expect* InvokeRequired to return
true.
So I think the program do not know where is the original UI
Thread....
Yes it does; all Controls know which thread owns them.
So I use Controls Method to change all of those label.
Fine - but you can only do this (including *finding* the control) once
you are on the UI thread. The bit I was drawing your attention to was
where you navigate to a sub-control (via Controls) in order to call
..Invoke; this is, quite simply, not safe. Just call .Invoke on the
form itself (all sub-controls *must* be owned by the same thread), and
then in your code you can do whatever you like (once on the UI
thread). My example did this, while still manipulating your dynamic
Controls; same result, but bomb-proof.

Marc
 
L

LamSoft

The UI Thread(Main Thread):

Thread myThread = new Thread(new
ThreadStart(scanStructure));
myThread.Start();

scanStructure():

public void scanStructure() {
// Doing some non-UI stuff..

foreach (DictionaryEntry de in myHashTable) {
updateBuildingDetail(de.Key.ToString(), de.Value.ToString());
}
}

then updateBuildingDetail is called by a child thread, and i think the main
thread is continuously run following codes and won't wait for the child
thread.

Marc Gravell said:
updateBuildingDetail is called by a child (non-UI) thread
And what is the UI thread doing at this point? Has it returned to it's
normal state? The following is typical bad code that will lead to
deadlocks (and is pointless):

SomeUIMethod() {
...spawn some non-UI work...
...wait for the above to finish / set a flag... // BAD
}

You need to exit the SomeUIMethod to let the UI process it's events. There
is also a hacky way to accomplish this, but I don't recommend it to the
extent that I won't even volunteer what it is (everybody knows the method
I mean ;-p).
I found that in the child thread, the invokeRequired of
tabBuildings.Controls["lblBuild_Resource_" + key] is already true..

When asking from a non-UI thread, we *expect* InvokeRequired to return
true.
So I think the program do not know where is the original UI Thread....
Yes it does; all Controls know which thread owns them.
So I use Controls Method to change all of those label.
Fine - but you can only do this (including *finding* the control) once you
are on the UI thread. The bit I was drawing your attention to was where
you navigate to a sub-control (via Controls) in order to call .Invoke;
this is, quite simply, not safe. Just call .Invoke on the form itself (all
sub-controls *must* be owned by the same thread), and then in your code
you can do whatever you like (once on the UI thread). My example did this,
while still manipulating your dynamic Controls; same result, but
bomb-proof.

Marc
 
M

Marc Gravell

and i think the main thread is continuously run

Meaning what? What does the code do after myThread.Start()? It should
return to the UI, letting the form paint itself, and process its
message-queue (which is necessary for Invoke/BeginInvoke). It it sits
in a tight-loop, then this approach is doomed (short of hacky hacks).

Re the arguments exception, do you get this using the MethodInvoker
(captured variables) approach? This is often a far easier way of
creating such a delegate... and *in this case* is entirely safe since
the strings are immutable, and the variables aren't updated inside the
method (there are some gotchas to look for).

Marc
 
L

LamSoft

Thank you very much, I solve the problem by creating another class and move
all the function to that class...
So weird~~
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top