webForumDet fria alternativet

ContextSwitchDeadlock

6 svar · 891 visningar · startad av Addeladde

AddeladdeMedlem sedan jan. 20013 406 inlägg
#1

Vad betyder detta?

CLR kunde inte övergå från COM-kontext 0x1c0dd0 till COM-kontext 0x1c0f40 under 60 sekunder. Tråden som äger målkontexten eller målinneslutningen utför troligen en icke-pumpande vänteåtgärd eller behandlar en mycket lång åtgärd som körs utan att pumpa Windows-meddelanden. Detta ger vanligtvis sämre prestanda och kan leda till att programmet slutar svara eller till att minnesanvändningen ökar med tiden. Du kan undvika problemet genom att låta alla enkeltrådade inneslutningstrådar (STA) använda pumpande vänteprimitiver (t ex CoWaitForMultipleHandles) och rutinmässigt pumpa meddelanden under åtgärder som körs under lång tid.

Får det på denna rad:

db.ExecuteNonQuery("SP_Sync_L_Insert");

Jag går igenom en collection av object och kör insert på varje objekt.

GladhMedlem sedan maj 20012 812 inlägg
#2

Som jag tolkar information som står i meddelandet, så har du en WinForm applikation där du i din huvudtråd (alltså den tråd som kör din WinForm applikation) och någonstans i denna tråd (säkert när man tryckt på en knapp) så gör du en massa databasanrop och dessa anrop tar sådan långtid att huvudtråden inte hinner göra något annat under dessa 60 sekunder.

I VB så tror jag man har något som heter DoEvents() som du skulle kunna lägg efter varje databasanrop i din for-loop. Jag vet dock inte om det finns något liknande i C#

Den bästa lösningen är dock att göra alla dessa anrop i en annan tråd, så du inte "fryser" din fönster, för när du gör dina databasanrop, så kan din applikation inte göra något annat, och det känns som om din applikation har hängt sig, för inget uppdateras på den.

- M

NickemannenMedlem sedan aug. 20003 575 inlägg
#3

Gladh skrev:

Som jag tolkar information som står i meddelandet, så har du en WinForm applikation där du i din huvudtråd (alltså den tråd som kör din WinForm applikation) och någonstans i denna tråd (säkert när man tryckt på en knapp) så gör du en massa databasanrop och dessa anrop tar sådan långtid att huvudtråden inte hinner göra något annat under dessa 60 sekunder.

I VB så tror jag man har något som heter DoEvents() som du skulle kunna lägg efter varje databasanrop i din for-loop. Jag vet dock inte om det finns något liknande i C#

Den bästa lösningen är dock att göra alla dessa anrop i en annan tråd, så du inte "fryser" din fönster, för när du gör dina databasanrop, så kan din applikation inte göra något annat, och det känns som om din applikation har hängt sig, för inget uppdateras på den.

- M

Menar du typ BackroundWorker som finns i System.ComponentModel namespace't?

AddeladdeMedlem sedan jan. 20013 406 inlägg
#4

Application.DoEvents(); finns men förhindrar inte från att applikationen ska sluta svara under tiden. Det är precis som Gladh säger att det körs ett anrop som tar lång tid och får applikationen att sluta svara. I debuggern så finns en inställning om att generera ett exeception när detta händer. Går dock att ändra denna inställning.

Det bästa är så klart att köra allting i en ny tråd.
Verkar dock vara rätt så klurigt att köra saker trådat? Eller har jag fått allt om bakfoten.

Min funktion anroppar en mängd statiska collections som ligger i huvudformen som alltid måste vara tillgängligt i programmets minne.

NickemannenMedlem sedan aug. 20003 575 inlägg
#5

Addeladde skrev:

Application.DoEvents(); finns men förhindrar inte från att applikationen ska sluta svara under tiden. Det är precis som Gladh säger att det körs ett anrop som tar lång tid och får applikationen att sluta svara. I debuggern så finns en inställning om att generera ett exeception när detta händer. Går dock att ändra denna inställning.

Det bästa är så klart att köra allting i en ny tråd.
Verkar dock vara rätt så klurigt att köra saker trådat? Eller har jag fått allt om bakfoten.

Min funktion anroppar en mängd statiska collections som ligger i huvudformen som alltid måste vara tillgängligt i programmets minne.

Det är precis detta som BackgroundWorker klassen hjälper dig med ;).

GladhMedlem sedan maj 20012 812 inlägg
#6

Addeladder skrev:

Application.DoEvents(); finns men förhindrar inte från att applikationen ska sluta svara under tiden.

Nope inte under tiden som du gör anropet till databasen, men om du lägger det efter anropet i din loop, så hinner applikationen få lite väntetid innan nästa anrop görs. Men om det hjälper har jag faktiskt ingen anning om, det är bara på pappert det ser bra ut ;)

addeladder skrev:

Det bästa är så klart att köra allting i en ny tråd.
Verkar dock vara rätt så klurigt att köra saker trådat? Eller har jag fått allt om bakfoten.

Det är inga problem med att köra saker trådat, bara att lägga koden som du vill få exekverat i en egen tråd i en funktion, så sedan använder du följande kod för att starta tråden.

Thread myThread = new Thread(new ThreadStart(HÄR SKRIVER DU IN NAMNET PÅ FUNKTIONEN));
myThread.Bakgroundthread = true;
myThread.Name = "My Thread";
myThread.Start;

Så nu gör all kod i din funktion i en egen tråd, enkelt va!?!?!?.

MEN, MEN, MEN... nu börjar problemen med multitrådade applikationer. Dels så får du inte uppdaterar något i din applikationsGUI från din tråd, du får alltså inte skriva

lblTime.Text = DateTime.Now.ToString()

i den kod som körs i egen tråd. Det kommer din applikation inte tycka om, tror att MS har lagt till ett exceptions för det i 2.0, det finns iallafall garanterat med från 3.0. Det är inte så att det inte fungerar, gör du det i 1.1/1.0 kod så fungerar det i 99,99% av alla dina tester, men när det kommer lite mer last på din applikation så kommer du upptäcka märkliga fel som du kommer ha svårt att hitta.

Så all uppdatering av kontroller i GUI måste ske i main-tråden.

Det andra problemet som är ett större problem, är att någon annan tråd kan komma in och ändra i din collection under tiden som du loopar igenom den, och detta kommer ge dig kritiska fel. Det kan även vara så att 2 trådar försöker skriva till samma minnesrymd samtidigt vilket inte kommer uppskattas av .NET och kommer ge dig konstiga exceptions som du kommer ha svårt att hitta, dessutom så kanske det lyckas, men datan blir korrupt....

BackGroundWorker thread hjälper dig att lösa problemet med att uppdatera till ditt GUI. Tyvärr hjälper den dig inte med att flera trådar försöker accessa samma data samtidigt, då måste man börja med lock och mutex, för att förhindra så att olika trådar kan accessa samma data samtidigt, och det är faktistkt betydligt mer komplicerat än man tror...

Jag har just detta problem i en WPF applikation som jag håller på med. Jag har en egen skriven collectionklas som är "binded" till en ListView och med hjälp av olika Interface och events så är det fixat så att om man ändrar i något i collection så kastar den event upp till listviewn att något är ändrat och listviewn uppdaterar sig själv. I en enkel trådad applikation är detta inga problem, och man kan använda sig av standard collection som finns med i .NET. Men så fort man uppdaterar collection från en annan tråd, så får man ett fel. Det är nu löst genom att se till så att uppdateringen sker i applikations egen tråd, där av den egenskrivna collection, för det klara ingen av MS standard kollektionern.

Då inträffar problemet om du har en tråd som lägger till objekt och en annan som tar bort objekt från collection. Innan den tråden som har lagt till objektet är riktigt färdig med alla notificeringar som den skall göra och innan listview hunnit uppdaterar sig och visa det nya objektet så har min tråd som tar bort objekt hunnit ta bort objektet från collectionen. Så när listview efterfrågar det nya objektet från collectionen för att visa det i kontrollen så finns det inte där längre och vips så har vi en exception.

Enkelt trodde jag, bara att göra lite lock på rätt ställen så löser det sig, första försöket slutade i deadlocks (alltså att 2 olika trådar står och väntar på varandra att bli färdig, eller till och med att samma tråd väntar på sig själv eftersom den råkar stötat på samma lock) efter ha hackat om lite så tycker jag att alla problem är lösta och gör en test, fungerar bra, gör lite mer testar allt fungerar bra, tills jag tryckte ner så att det skapas ett nytt objekt var 1-5 millisekund, och var 1-5 millisekund så tar man bort ett objekt och var 1-5 millisekund så uppdaterar jag ett objekt från listan. Det fungerarde bra i några tusen objekt, sedan poff... Kunde inte hitta objektet i collection.

Och jag måste säga att jag tycker att jag gjort allt rätt och kan inte se hur objektet kan bli borttaget innan alla uppdateringar är färdiga... och det suger fett. men det är iallfall ett betydligt framsteg i från första försöket (y)

- M

AddeladdeMedlem sedan jan. 20013 406 inlägg
#7

Japp jag började köra trådat igår kväll och testatde Delegate och Invoke för att få saker i GUI att fungera. Krångligt men framför allt så mycket extra kod för att bara databinda ett datagridview.

Din problem Gladh verkar inte vara speciellt roliga de heller.

134 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
132 ms — hämta tråd, inlägg och bilagor (db)