Autor Beitrag
navigato
Hält's aus hier
Beiträge: 14
Erhaltene Danke: 1

Win7
C#,Firebird
BeitragVerfasst: Fr 08.10.10 15:30 
Hallo,

ich verzweifle an einer Thread-Problematik, deren Lösung ich weder hier im Forum noch im Internet gefunden habe:
In einem WPF-Programm soll eine Frage-MessageBox aufgerufen werden, die auch einen Rückgabewert (könnte in einer statischen Variable erfolgen, das ist aber hier nebensächlich) liefern soll. Der Aufruf der MessageBox befindet sich in einer Methode "FrageImRichtigenThreadZeigen". Initiiert wird das Geschehen per WCF-Callback von einem anderen Programm. Da "meine" MessageBox ein WPF-Element ist, muss die MessageBox im UI-Thread ablaufen, in dem sich der Aufruf von "außen" aber nicht befindet. Zu diesem Zweck bietet sich "this.Dispatcher.BeginInvoke" (asynchron) oder "this.Dispatcher.Invoke" (synchron) an. Folgenden Aufruf habe ich durchgeführt:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
string strFrageText="Wird dieses Problem jemals gelöst?";

DispatcherOperation objDispOp = this.Dispatcher.BeginInvoke(new DelegatTypString(FrageImRichtigenThreadZeigen),
                                                System.Windows.Threading.DispatcherPriority.Normal, strFrageText);

//und nun soll gewartet werden

In der Methode soll nach dem BeginInvoke-Aufruf gewartet werden, bis die aufgerufene Methode "FrageImRichtigenThreadZeigen" fertig ist. Also habe ich statt "BeginInvoke" "Invoke" genutzt. Problem dabei: Das Programm bleibt stehen, die MessagePump scheint zu beschäftigt zu sein...
Also doch BeginInvoke? Problem dabei: wenn statt des Kommentars "//und nun soll gewartet werden" der Thread angehalten oder per folgendem Code
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
while (objDispOp.Status == DispatcherOperationStatus.Pending)
{
    MeinePersoenlicheLogMethode("Pending");
    Thread.Sleep(10);
}

gewartet wird, wird die Methode "FrageImRichtigenThreadZeigen" niemals aufgerufen, der Status bleibt immer "Pending"!

Die MSDN bietet hier die Methode
ausblenden C#-Quelltext
1:
objDispOp.Wait();					

an. Das ist aber in meinem Fall glatt gelogen, denn auch dann bleibt das Prgramm stehen, ohne dass der Status "Pending" jemals geändert wird.


Meine Frage ist also: Wie kann man hier warten und die Methode "FrageImRichtigenThreadZeigen" trotzdem starten? Für eine Antwort wäre ich extrem dankbar :wink:
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Fr 08.10.10 15:53 
Ausgeführt wird der im BeginInvoke angegebene Code aber schon? Dann dürfte es ja eigentlich keinen Grund geben, warum nicht auch Invoke funktionieren sollte.
Ansonsten müsstest du dir mal die Call Stacks der beiden Threads zum Zeitpunkt des Deadlocks anschauen.

_________________
>λ=
navigato Threadstarter
Hält's aus hier
Beiträge: 14
Erhaltene Danke: 1

Win7
C#,Firebird
BeitragVerfasst: Fr 08.10.10 20:08 
Zitat:

Ausgeführt wird der im BeginInvoke angegebene Code aber schon?

Nein, wenn die Methode, in der BeginInvoke aufgerufen wird, nach der Methode BeginInvoke nicht beendet wird, startet auch die Methode, die von BeginInvoke aufgerufen werden soll, niemals. Der Status des aus BeginInvoke resultierenden DispatcherOperation-Objekts ist und bleibt für alle Zeit "pending". Wenn die Methode, in der BeginInvoke aufgerufen wird, nach diesem Befehl keinen Code mehr enthält, dann wird auch die Methode, die von BeginInvoke aufgerufen werden soll (hier "FrageImRichtigenThreadZeigen"), tatsächlich gestartet.

Daher habe ich folgende grundsätzliche Frage: Ist es so, dass "normalerweise" die Methode, die von BeginInvoke aufgerufen wird (hier "FrageImRichtigenThreadZeigen"), immer auch gestartet wird? Muss dafür die Methode, die BeginInvoke enthält grundsätzlich zunächst beendet werden? Oder kann man diese absichtlich nicht beenden und der aufzurufenden Methode (hier "FrageImRichtigenThreadZeigen") irgendwie auf die Beine helfen (so etwas wie System.Windows.Forms.Application.DoEvents())?

Zitat:
Dann dürfte es ja eigentlich keinen Grund geben, warum nicht auch Invoke funktionieren sollte.

Dachte ich auch...

Zitat:
Ansonsten müsstest du dir mal die Call Stacks der beiden Threads zum Zeitpunkt des Deadlocks anschauen.

Vielen Dank für den Tipp! Klingt toll, habe nur spontan keine Ahnung wie das funktionieren sollte...
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Fr 08.10.10 21:29 
user profile iconnavigato hat folgendes geschrieben Zum zitierten Posting springen:
Nein, wenn die Methode, in der BeginInvoke aufgerufen wird, nach der Methode BeginInvoke nicht beendet wird, startet auch die Methode, die von BeginInvoke aufgerufen werden soll, niemals.
Dann ist klar, dass der GUI-Thread auf etwas im Nebenthread wartet, was ja eigentlich nicht sein dürfte. Also: Halte den Prozess in Visual Studio an, sobald der Deadlock anscheinend eingetreten ist, und schau dir unter "Debug->Windows->Threads" unter "Location" den Stack Trace der beiden Verdächtigen an. Bei einem Code wie
ausblenden C#-Quelltext
1:
Task.Factory.StartNew(() => this.Invoke(new Action(() => { }))).Wait();					

sehe ich z.B. einen WorkerThread mit
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
                 [In a sleep, wait, or join]   
                  mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) + 0x2b bytes  
...
                  System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate method, object[] args) + 0x50 bytes   
                  System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate method) + 0x7 bytes   
                  WindowsFormsApplication1.exe!WindowsFormsApplication1.Form1.Form1_Load.AnonymousMethod__0() Line 23 + 0x5b bytes

und im Main Thread
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
                              [In a sleep, wait, or join]     
                              mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout, bool exitContext) + 0x18 bytes     
...
                              mscorlib.dll!System.Threading.Tasks.Task.Wait() + 0xb bytes     
                              WindowsFormsApplication1.exe!WindowsFormsApplication1.Form1.Form1_Load(object sender = {WindowsFormsApplication1.Form1}, System.EventArgs e = {System.EventArgs}) Line 23 + 0x45 bytes     
...
                              System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x61 bytes     
                              System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) + 0x31 bytes     
                              WindowsFormsApplication1.exe!WindowsFormsApplication1.Program.Main() Line 18 + 0x1d bytes    
...

Der Worker Thread hat in Invoke eine Window Message an den Main Thread geschickt, damit dieser den Invoke-Code abarbeitet, und wartet nun anscheinend über ein WaitHandle darauf, dass das geschehen ist. Der Main-Thread ist aber von RunMessageLoop und damit dem Abarbeiten der Window Message weit entfernt, sondern wartet lieber auf Beendigung des Nebenthreads. Oops ;) .

_________________
>λ=
navigato Threadstarter
Hält's aus hier
Beiträge: 14
Erhaltene Danke: 1

Win7
C#,Firebird
BeitragVerfasst: Fr 08.10.10 22:39 
Zitat:
Debug->Windows->Threads" unter "Location"

kannte ich nicht! :oops:
Aber das ist absolut genau der richtige Hinweis gewesen: Mein Problem ist nämlich an einer völlig anderen Stelle verursacht worden, als ich das vermutet habe und das kann man in dem Thread-Überwachungsdialog super erkennen. Der Main-Thread wartet nämlich tatsächlich auf die Beendigung einer völlig anderen Methode.

Also kann ich meine Frage
Zitat:
Ist es so, dass "normalerweise" die Methode, die von BeginInvoke aufgerufen wird (hier "FrageImRichtigenThreadZeigen"), immer auch gestartet wird? Muss dafür die Methode, die BeginInvoke enthält grundsätzlich zunächst beendet werden?

nun selber beantworten: Die Methode, die von BeginInvoke im Main Thread aufgerufen wird (hier "FrageImRichtigenThreadZeigen"), wird nur dann dort auch ausgeführt, wenn der Main Thread nicht gerade endlos beschäftigt ist! Ist dieser nicht beschäftigt, dann wird die aufzurufende Methode auch problemlos gestartet, auch wenn die Methode die BeginInvoke enthält, nach dem Aufruf endlos wartet.

Ein riesiges Dankeschön an dich, das ist das gute Ende von sehr viel Aufwand den ich in die Anzeige einer simplen MessageBox gesteckt habe!