Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien
Toon posts:

C# void awaitable maken

Pagina: 1
Acties:

Verwijderd

Topicstarter
Beetje vaag misschien dus ik zal het een beetje proberen te omschrijven.
Ik heb een applicatie geschreven met een pluginsysteem en er zijn derden die plugins maken.
Nu ben ik zo zoetjes aan in wat libraries async (TAP pattern) functionaliteit aan het toevoegen, so far so good.
Nu wil een pluginbouwer die ook daadwerkelijk gaan gebruiken en daar loopt het spaak.
In de plugininterface (die ik aanlever) hebben de methods geen Task returntypes. Ook zitten er voids tussen.
De aanroepende code (aan mijn kant) loopt dat soort calls dan ook synchroon af.
Als de pluginbouwer zijn signature aanpast om bijv een void async te maken zodat ie in zijn code een await kan gebruiken denkt de code aan mijn kant dat ie meteen klaar is.
Ik snap ook wel hoe dat komt enzo, maar zit nu effe met een dilemma.

Plugin interface aanpassen zodat alles Task(T) wordt is niet ideaal omdat dan alle pluginbouwers moeten upgraden.
Tweede interface voor een soort v2 plugin doe ik liever (nog) niet.

Is er een manier hoe een pluginbouwer Tasks kan gebruiken zonder dat mijn code erover valt?
Of is er een manier om te detecteren vanuit mijn kant dat er async gebruikt wordt?
Dat laatste zal wel niet, maar wie weet heeft er iemand met hetzelfde bijltje gehakt

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

Met "een void" bedoel je gewoon een implementatie van een void-methode?
Wat ik uit je post begrijp wil je dus een methode van iemand z'n plugin aanroepen, maar zou het kunnen zijn dat de plug-in bouwer deze asynchroon implementeert(een nieuwe thread/task start) en de call dus returned voordat 'het werk' gedaan is?

Als je niets aan de bestaande interfaces wil veranderen kun je erover denken om een extra callback interface te leveren. De plugin roept dan de callbacks aan op het moment dat het werk gedaan is.

Overigens vraag ik me af wat het nut van asynchroon implementeren van de plug-in interface is als jouw code er uiteindelijk toch op gaat wachten..

Verwijderd

Kun je niet gewoon een extra interface definieren waarmee aangegeven kan worden welke API calls volgens de pluginbouwer asynchroon gedraaid moeten worden?

Je kunt vervolgens aan jouw kant de asynchrone verwerking inbouwen zonder de originele API aan te passen. Je hoeft dan namelijk alleen te kijken of de Async interface geimplementeerd is en deze aanroepen.

Een paar voordelen die ik daarvan zo zie zijn dan dat jij ervoor kan kiezen om de volledige controle te hebben over de async processen en voor de pluginbouwer kan hij elke aanroep, al dan niet bedoeld als async, op exact dezelfde manier bouwen.

Op die manier is het ook geen v2 van je API, maar meer een extensie op je v1. (Snap je wat ik bedoel? Ik heb het idee dat het best vaag is en dat ik nog wat caffeine kan gebruiken :P)

[ Voor 18% gewijzigd door Verwijderd op 11-03-2014 11:18 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Je kan een call in een Task wrappen, maar dan wordt de call gewoon op een andere thread afgehandeld.
Dan krijg je dus eigenlijk de overhead van een context switch voor weinig voordeel.

Lees ook even dit voor meer achtergrond over async/await: MSDN: Async/Await - Best Practices in Asynchronous Programming

Verwijderd

HMS schreef op dinsdag 11 maart 2014 @ 13:31:
Je kan een call in een Task wrappen, maar dan wordt de call gewoon op een andere thread afgehandeld.
Dan krijg je dus eigenlijk de overhead van een context switch voor weinig voordeel.

Lees ook even dit voor meer achtergrond over async/await: MSDN: Async/Await - Best Practices in Asynchronous Programming
Dat is wat mooier verwoord qua richting die ik ook bedoelde :P

Verwijderd

Topicstarter
EddoH schreef op dinsdag 11 maart 2014 @ 10:32:
Met "een void" bedoel je gewoon een implementatie van een void-methode?
Wat ik uit je post begrijp wil je dus een methode van iemand z'n plugin aanroepen, maar zou het kunnen zijn dat de plug-in bouwer deze asynchroon implementeert(een nieuwe thread/task start) en de call dus returned voordat 'het werk' gedaan is?
Exact
Als je niets aan de bestaande interfaces wil veranderen kun je erover denken om een extra callback interface te leveren. De plugin roept dan de callbacks aan op het moment dat het werk gedaan is.
Maar als ik pas weer verder ga op het moment dat de callback aangeroepen wordt moeten alle plugins een update krijgen en betekent dus effectief een wijziging in de bestaande interface.
Overigens vraag ik me af wat het nut van asynchroon implementeren van de plug-in interface is als jouw code er uiteindelijk toch op gaat wachten..
Ik snap dat het vreemd lijkt. In de plugins gebeuren redelijk wat internet acties, zoals dingen downloaden/scrapen etc. Dingen die uitstekend geschikt zijn om async tegenaan te programmeren. Wat er nu gebeurt is dus aan de pluginkant dat de bouwers dingen gaan maken als busyloops die af en toe forceren dat de messages op de UI thread geprocessed worden om deadlocks te voorkomen. Heel erg lelijk allemaal.
Vandaar de wens voor async.
Verwijderd schreef op dinsdag 11 maart 2014 @ 11:17:
Kun je niet gewoon een extra interface definieren waarmee aangegeven kan worden welke API calls volgens de pluginbouwer asynchroon gedraaid moeten worden?

Je kunt vervolgens aan jouw kant de asynchrone verwerking inbouwen zonder de originele API aan te passen. Je hoeft dan namelijk alleen te kijken of de Async interface geimplementeerd is en deze aanroepen.

Een paar voordelen die ik daarvan zo zie zijn dan dat jij ervoor kan kiezen om de volledige controle te hebben over de async processen en voor de pluginbouwer kan hij elke aanroep, al dan niet bedoeld als async, op exact dezelfde manier bouwen.

Op die manier is het ook geen v2 van je API, maar meer een extensie op je v1. (Snap je wat ik bedoel? Ik heb het idee dat het best vaag is en dat ik nog wat caffeine kan gebruiken :P)
Een tweede interface implementeren, hmm, zou best kunnen werken
Een soort 'halve' v2. Betekent wel meer clutter aan mijn kant, maar misschien is dat de beste manier.
HMS schreef op dinsdag 11 maart 2014 @ 13:31:
Je kan een call in een Task wrappen, maar dan wordt de call gewoon op een andere thread afgehandeld.
Dan krijg je dus eigenlijk de overhead van een context switch voor weinig voordeel.

Lees ook even dit voor meer achtergrond over async/await: MSDN: Async/Await - Best Practices in Asynchronous Programming
In een Task wrappen is geen optie want dan moeten de signatures gewijzigd worden. Overigens wordt een Task niet per definitie op de threadpool gezet. Je kan 'm ook synchroon draaien.
De werking van Tasks ben ik wel een beetje bekend mee, maar staan nog wel handige tips achter die link, dus evengoed bedankt.

Verwijderd

Verwijderd schreef op dinsdag 11 maart 2014 @ 15:19:

[...]
In een Task wrappen is geen optie want dan moeten de signatures gewijzigd worden. Overigens wordt een Task niet per definitie op de threadpool gezet. Je kan 'm ook synchroon draaien.
De werking van Tasks ben ik wel een beetje bekend mee, maar staan nog wel handige tips achter die link, dus evengoed bedankt.
Dat zou niet perse moeten hoeven hoor. Jouw calling kant moet gewoon de juiste parameters doorgeven in de task waarmee jij hem encapsulate.

Verwijderd

Topicstarter
Oh aan mijn kant wrappen?
Maar dan nog returned mijn task dan toch meteen omdat de void in dit voorbeeld geen task retouneert?
Daarbij voorzie ik issues met thread safety als ik zomaar alles in een andere thread ga mikken.

Zal even een voorbeeld schetsen van hoe een plugin kan werken. Niet te letterlijk nemen.
Stel je voor dat ik een formulier heb waarin informatie getoond wordt over een boek.
De prijs is niet actueel maar er is een plugin die iemand gebouwd heeft die de laatste prijs van bol.com ophaalt.
Vanaf mijn kant roep ik dan aan plugin.GetLatestPrice()
Die call is gewoon synchroon en wil meteen een labeltje updaten of iets dergelijks.
De plugin bouwer gaat scrapen door een hele webbrowser erop te mikken die volgens het EAP pattern werkt. Hij wil echter voor het einde van de method resultaat geven dus houdt zichzelf bezig in een loop terwijl ie op bepaalde events wacht om zodoende de volgende actie uit te kunnen voeren alvorens tot een resultaat te komen.
Met loop zorgt ie voor deadlocks omdat ie de UI thread bezighoudt etc waar ie weer omheen moet werken.
Kortom: gedoe en dan zegt zo'n pluginbouwer: "het zou handig zijn als ik gewoon dit in mijn code kan aanroepen"
code:
1
2
3
await browser.NavigateAsyc("http://bol.com");
browser.Execute("somejavascripthere") ;
return browser.Execute("codevoorlabel");


Dat kan ie allemaal bewerkstelligen, maar om await te gebruiken moet ie wel effe
decimal GetLatestPrice()
veranderen in
async decimal GetLatestPrice()

Maar dat gaat natuurlijk niet zomaar.
Dan kan ik wel aan mijn kant iets gaan aanroepen van await Task.Run<decimal>(() => plugin.GetLatestPrice()); maar dan wacht ie toch echt niet op resultaat van GetLatestPrice en ik snap ook wel waarom, daar gaat het niet om.

Eigenlijk zou er voor dit soort situaties ook een GetLatestPriceAsync method moeten zijn in de plugin, maar wil niet elke pluginbouwer meteen opzadelen met upgraden terwijl maar een paar zo'n wens hebben.
Ik wil ook niet meteen v2 API en dan natuurlijk later volgend v3,4 en 5 en maar met alles compatible blijven.

Nu neig ik het meeste naar een complimenterende interface voor de Async calls.

  • IceM
  • Registratie: Juni 2003
  • Nu online
Wat ik niet zo goed begrijp is waarom de plugin bouwers zelf niet op een correcte manier wachten totdat het resultaat binnen is en dan returnen. Aangezien jij alles synchroon afhandelt heeft het totaal geen nut om aan jou kant een Task te accepteren waar je vervolgens direct op wacht. Alleen een asynchroon resultaat accepteren omdat plugin bouwers zelf niet weten hoe ze een busy wait moeten voorkomen lijkt me niet correct.

Volgens mij zou een plugin bouwer het zo op moeten kunnen lossen? (snippet van MSDN, ik ben een Java man).
C#:
1
2
3
4
5
6
7
8
9
10
private static async Task DelayAsync()
{
    await Task.Delay(1000);
}

public static void PluginCall()
{
    var delayTask = DelayAsync();
    delayTask.Wait();
}

...


  • Grijze Vos
  • Registratie: December 2002
  • Laatst online: 28-02 22:17
Als jouw library synchroon werkt, waarom wil je dan uberhaupt een aysnchrone API aanbieden?

Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info


Verwijderd

Verwijderd schreef op dinsdag 11 maart 2014 @ 16:25:
Oh aan mijn kant wrappen?
Maar dan nog returned mijn task dan toch meteen omdat de void in dit voorbeeld geen task retouneert?
Daarbij voorzie ik issues met thread safety als ik zomaar alles in een andere thread ga mikken.
Integendeel, je thread safety wordt er alleen maar beter op wanneer jij de threads managed ipv de pluginbouwer. En de werking van een .NET Task is niet anders of jij hem implementeert dmv wrapping of de pluginbouwer direct, dus je moet je afvragen of async dan uberhaubt wel een optie is? (Alhoewel ik je vraag niet goed begrijp, aangezien een void duurt zolang hij duurt en je gewoon op tasks kunt wachten door middel van calls ed.).

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
Dit lijkt me een slecht idee, je wilt juist dat de pluginbouwers hun thread architectuur kunnen kiezen. Zij weten immers wat voor soort taak er gaat gebeuren. Als jij voor de pluing bouwers alles naar andere threads gaat marshallen kom je ook in een soort van "CrossThreadException" hell. Je kunt immers de plugin bouwers niet tegen houden threads te gebruiken.

Ik denk dat dit echt een fundamenteel probleem met je API is en dat je hiervoor best een nieuw stuk API mag toevoegen.

@Grijze Vos
Het synchroon uitvoeren is al helemaal geen oplossing omdat dan de applicatie zelf freezed. Dat je applicatie synchroon werkt betekend nog niet dat een plugin synchroon hoeft te werken. Stel je bijvoorbeeld een media player voor (kan heel synchroon) en een plugin die subtitles van internet download. Dat wil je toch echt asynchroon doen ook al is de media player synchroon.

~ Mijn prog blog!

Pagina: 1