Försöker implementera samma funktionalitet som Control.Invoke(MethodInvoker) använder sig av. Det finns ju nå'n "threaddata" som du skulle kunna använda tror jag... Se till så att den alltid innehåller en egenskriven klass med lite events i... På så sätt kan huvudtråden lägga till en eventhandler för ett event i den klassen med nå't kommandon (AddHandler i vb.net iaf.) och arbetstråden kan låta eventet i klassen "gå av" när delegaten skall över... Jag har inte en aning om ifall det här är trådsäkert, men du lär iaf. behöva gör en lock på klassen först... nu gissar jag villt här men. >jaja det är nog bäst att låta sjögren ta denna ;-) <b>Onkelborg:</b> om jag signalerar ett event från en arbetstråd så kommer även eventanteraren att exekveras på den tråden, inte på huvudtråden. Tyvärr verkar många ha missuppfattat hur trådning och delegater fungerar tillsammans. Hm... Events skall väl gå över flera trådar om jag inte tar helt fel? Alltså, om du har två trådar, a och b, och du i tråd b exekverar ett event, då kommer även eventhanteraren (den som du i vb markerat med handles och i C# mer intuitivt lagt till i invokeringslistan med +=) att exekveras av tråd b. Nu har jag inte testat detta och det är en vild gissning, du kanske skulle kunna lösa det hela med en singelton där du placerar din huvudtråd? det skulle lösa det hela hur? hmm, tror inte min teori funger nu när jag tänker efter. Jo resetevents kan lösa det, men då måste jag skapa en egen message loop och via waithandles eller resetevents signalerar meddelande loopen att ett nytt meddelande kommit. Funkar fint i consol applikationer men inte i windows formulär eller windows services och jag är lite ute efter en generell lösning som sagt. >Mattias: Har du någon länk till mer information om det api'et? får man fråga vad det är du ska göra? Jag skriver ett par generella funktionsbibliotek, i flera klasser i de här bilioteken så vill jag att klienter skall få prenumerera på händelser och i många fall är de här klasserna multi-trådade. Mattias: Svarar mig själv. Flytta över exekvering till annan tråd.
Jag vill alltså ifrån en arbetstråd (bakgrundstråd) skicka en delegat till huvudtråden och låta instruera huvudtråden att exekvera delegaten. I praktiken är det en "Execution marshalling" jag är ute efter.
Jag har spårat funktionalitet i Control.Invoke till en metod som heter MarshalInvoke. Den verkar var skriven i unmanaged c++ på något sätt, har fastnat där.
Frågan är alltså, hur ska jag lyckas skicka ett delegatobjekt från en tråd till en annan och låta den senare exekvera delegaten?Sv: Flytta över exekvering till annan tråd.
Sv: Flytta över exekvering till annan tråd.
är det inte så att control.invoke inte flyttaröver exekveringen utan snarare bakom kulisserna skickar ett meddelande till kontrollens wndproc
som sedan processas av huvudtråden , den hittar meddelandet och med wparam/lparam så får den en pekare till någon strukt som innehåller delegaten som ska anropas.
om det är så , så går det bara göra detta om det finns något som processar fönster meddelanden..
men som sagt det är en gissning..
för att fortsätta att inte svara på din fråga och bara gissa lite mer.
kan du inte ha en arraylist som är syncad mellan trådarna och i din bakgrundstråd fylla listan med delegate som ska anropas
och sedan låta huvudtråden polla listan och anropa dessa delegater?
//Roger
ps.
jaja det är nog bäst att låta sjögren ta denna ;-)Sv: Flytta över exekvering till annan tråd.
Har inte så mycket att tillägga, det du skrev låter korrekt. Control.Invoke är väl implementerad med API:et PostThreadMessage om jag kommer ihåg rätt.
MS
Sv: Flytta över exekvering till annan tråd.
Jag har fått flera förslag om just det här med events och det verkar som den allmänna uppfattningen är att när en eventdelegat exekveras så är det huvudtråden som hanterar den, det stämmer inte.
Det är alltid tråden som invokerar delegaten (vare sig det är en "vanlig delegat" eller en eventdelegat), som sedan exekverar metoden den pekar på (eller det är inte riktigt sant heller när det gäller asynkron exekvering av delegater, men då sker det på ännu en ny arbetstråd och inte på huvudtråden).
<b>Roggan:</b> Det är absolut en snygg workaround, men fortfarande bara en workaround ;) Man kan tänka sig att bygga en egen messagepump och använda System.Collections.Queue för att köa meddelanden till huvudtråden, men hur pollar man enklast en sådan kö utan att huvudtråden låses upp? Man vill ju att den skall fortsätta att exekvera övrig kod.
<b>Mattias:</b> Har du någon länk till mer information om det api'et? Hade det inte varit för att System.Threading.Thread var sealed så skulle man ju kunna utöka den vidare med liknande funktionalitet.Sv: Flytta över exekvering till annan tråd.
Sv: Flytta över exekvering till annan tråd.
Detta är ett problem med windowsformulär bland annat, där man måste skicka alla delegater som skall uppdatera ngn av dina kontroller, via Control.Invoke för att anropet skall göras på huvudtråden.
Så gör du något i en bakgrundstråd och tex vill uppdatera en progressbar, då måste du använda dig av myForm.Invoke(new MethodInvoker(updateProgress)); för att det skall lyckas, du kan <b>inte</b> bara signalera ett event som uppdaterar progressbaren eftersom det då inte skulle ta bort prylen med att vi inte är på huvudtråden.
Och Ja, om du provar så kommer progressbaren att i 7 fall av 10 uppdateras utan problem, men nej det betyder inte att det är huvudtråden som gör det. Enklaste sättet att kolla det här är att sätta upp ett scenario med ett par trådar, signalera ett event och i Thread fönstret i VS.NET kolla vilken tråd som exekverar koden.
Det vill säga, events går <b>inte</b> över flera trådar. Vet inte var folk får det här magiska tänket runt events ifrån, de är helt enkelt metodpekare vi skapat och standariserat med hjälp av event nyckelordet och genom att följa designmönstret för eventhanterarna (event delegaten)Sv: Flytta över exekvering till annan tråd.
/Fredrik Normén NSQUARED2Sv: Flytta över exekvering till annan tråd.
menar du att jag skulle dra igång en singleton och lägga huvudtråden på en AutoresetEvent och vänta på signalering??? Eller hur då?Sv: Flytta över exekvering till annan tråd.
Kan detta ev lösa ditt probelem:
http://docs.msdnaa.net/ark_new3.0/cd3/content/Type_Sample%20Applications.htm#title5_13 ???
/Fredrik Normén NSQUARED2Sv: Flytta över exekvering till annan tråd.
Men verkar som jag får ge upp det så länge och bygga specifika lösningar beroende på klient *suck*Sv: Flytta över exekvering till annan tråd.
Nej tyvärr, inget förutom det du nog själv kan hitta i MSDN.
Du kan ju inte tvinga en existerande tråd att helt plötsligt avbryta det den håller på med och köra en eventhanterare, så jag tror du är tvungen att välja nån slags meddelandeloop eller pollningsliknande lösning, hur du än vrider på det.
MSSv: Flytta över exekvering till annan tråd.
det kanske går att vrida och vända på problemet på någe sätt :)
vad är det för jobb som ska köras i huvudtråden och vad är det för någe som ska anropas via delegater i arbetstråden?
tänkte om det går att göra något special special , så att det jobb som nu körs i huvudtråden flyttas in i ytterligare en tråd , och låta huvudtråden bara polla vad som skett i de båda arbetstrådarna..
//RogerSv: Flytta över exekvering till annan tråd.
Ibland kan dessa klienter vara windowsformulär, ibland är de inte det.
När de är windows formulär så vill jag att eventet skall triggras på huvudtråden, för att eventhanteraren skall kunna uppdatera kontroller och dylikt via den vanliga messagepumpen.
Som det är nu måste jag i min eventhangerare, <b>om</b> klienten är ett windowsforumulär, skapa en methodinvoker och använda myForm.Invoke för att uppdatera kontroller. Jag vill undvika det eftersom det då kräver mer från den som använder mina klasser.
Därför skulle jag vilja låta eventet triggras på huvudtråden, då spelar det ingen roll vad det är för klient eller vad klienten exekverar för kod vidare i händelsehanteraren.Sv: Flytta över exekvering till annan tråd.
I hear you, och självklart är det som du säger. En tråd är ju upptagen med något hela tiden om den lever, för en huvudtråd i ett formulår så innebär det ju att serva en messagepump och i en konsol applikation att exekvera main tills den avslutas.Sv: Flytta över exekvering till annan tråd.
Efter att ha blivit triggad av Andreas Håkanson så har vi lyckats leta fram ett svar.
Svaret är ett litet trick där vi använder data från event-delegaterna tillsammans med "runtime type casting"
Alla event har en sk "MulticastDelegate" som bas, en MulticastDelegat innehåller 1+ sk "SingelCastDelegate" som pekar på en enskild metod på ett enskilt objekt. Dessa delegater skapas när vi lägger till event-hanterare för vårt event. Bas-delegaten har en egenskap som heter <b>InvocationList</b> som är en lista på alla "SingelCastDelegater" vilka har bett om att bli signalerade när någon exekverar ett event.
Varje delegat innehåller dessutom egenskapen <b>Target</b>, den pekar på objektet i vilken metoden som vi skall signalera är definerad.
Tricket är att iterera igenom alla målmetoder som finns i <b>InvocationList</b> och helt enkelt avgöra om metoden är definerad i ett objekt som kräver synkroniserad-exekvering. För kontroller (forumulär, knappar osv) så är detta sant.
För att stödja synkroniserad-exekvering på dessa kontroller så har MS skapat interfacet <b>ISynchronizeInvoke</b> vilket definerar stöd för BeginInvoke och Invoke för att flytta exekveringen av delegater till huvudtråden som handhar meddelandepumpen för applikationen.
Vi nyttjar sedan <b>As</b> operatorn för att avgöra om objektet som delegaten pekar på implementerar nämnda interface, är så fallet exekverar vi eventet genom "Target" objektet istället för att använda den vanliga delegate syntaxen.
Nedan är ett exempel på hur det kan se ut:
protected void SyncInvoke(Delegate del, object sender, EventArgs e)
{
// Make sure that the delegate is instanciated
if (del != null) {
// Create an object array which will hold
// the arguments sent to the event handler
// function
object[] arguments;
arguments = new object[2];
arguments[0] = sender;
arguments[1] = e;
// --
// Loop through all delegates in the invocationlist to
// examine each tarhet object
foreach(Delegate d in del.GetInvocationList())
{
// Try to cast the tarhet object to a variable of the type
// ISynchronizeInvoke. If this succeeds the target object
// is probably a control.
ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
// Cast failed, invoke delegate the regular way.
d.DynamicInvoke(arguments);
}
else
{
// Cast succeeded, the target object is a control
// post the delegate to the main thread
syncer.BeginInvoke(d, arguments);
}
}
} // if (del != null)
} // SyncInvoke
// Event
public event WorkEvent Work;
// Method used from our class to invoke our event.
private void OnWorkDone(EventArgs e)
{
if(null!=Work)
{
this.SyncInvoke(Work, this, e);
}
}