Saturday, 15 February 2014

c# - Blocking getters while async task completes -



c# - Blocking getters while async task completes -

i load objects database. load object when need it, , in constructor phone call database , populate methods need to.

what had done speed process create task in constructor , utilize wait before returned value in getter. example

before:

class foo{ public string bar {get;set;} public foo(int id){ datarow res; //do database operations bar = res["bar"].tostring() } }

now:

class foo{ private task loadtask; private string _bar; public string bar { { loadtask.wait(); homecoming _bar; } set { _bar = value; } } public foo(int id){ loadtask = task.factory.startnew(() => { datarow res; //do database operations bar = res["bar"].tostring(); }); } }

what wanting extend class, in constructor fires off task, calls overridden method in sub class , block getting of property until task completes.

the found this, not sure if want @ all

http://www.gutgames.com/post/overridding-a-property-with-reflectionemit.aspx

as said earlier, think design can improved, appreciated technical challenge gave shot.

what talked sounded not dissimilar entity framework's dynamically created alter tracking proxies, had quick around frameworks work dynamic proxies , settled on castle project (http://www.nuget.org/packages/castle.core) weapon of choice.

naive implementation

this we're going @ stage:

foo foo = foo.factory.create<foo>(); foo.bar = "zzz"; // runs immediately. string bar = foo.bar; // blocks until initialisation has completed.

let's leave out inheritance (pretend foo sealed).

we want foo have no public constructors forcing consumer instantiate via foo.factory.create<foo>(), returns dynamic proxy derived foo additional bit of functionality injected every virtual property getter invocation: wait initialisation tasks complete.

using system.collections.generic; using system.threading.tasks; using castle.dynamicproxy; public class foo { // fields. protected readonly list<task> initialisationtasks = new list<task>(); // properties. // these have declared virtual // in order dynamic proxying work. public virtual string bar { get; set; } protected foo() { // initialisation work. this.initialisationtasks.add(task.delay(500)); } // responsible instantiating dynamic // proxies of foo , derivatives. public static class mill { // static fields. static readonly proxygenerator proxygenerator = new proxygenerator(); static readonly waitforinitinterceptor interceptor = new waitforinitinterceptor(); // mill method. public static t create<t>() t : foo { homecoming proxygenerator.createclassproxy<t>(interceptor); } class waitforinitinterceptor : iinterceptor { public void intercept(iinvocation invocation) { // applies getters only. if (invocation.method.name.startswith("get_")) { var foo = invocation.invocationtarget foo; if (foo != null) { // block until initialisation completes. task.whenall(foo.initialisationtasks).wait(); } // go on target method. invocation.proceed(); } } } } }

so far good, sound of we'll have deal inheritance. existing design not back upwards that, because:

the derived class can introduce public constructor thereby bypassing proxy creation via foo.factory.create<foo>() - need disallow that. any properties in derived type need declared virtual getter invocations can intercepted proxy.

tweaking back upwards inheritance

reflection rescue:

public class foo { // fields. protected readonly list<task> initialisationtasks = new list<task>(); // properties. // these have declared virtual // in order dynamic proxying work. public virtual string bar { get; set; } protected foo() { // enforce proxy integrity. this.validate(); // initialisation work. this.initialisationtasks.add(task.delay(500)); } private void validate() { var type = proxyutil.getunproxiedtype(this); // no public constructors. if (type.getconstructors().length != 0) { throw new invalidoperationexception( "public constructors not supported in derived types." ); } // no non-virtual properties. foreach (var property in type.getproperties()) { // we're interested in getters. var method = property.getgetmethod(); if (method != null && !method.isvirtual) { throw new invalidoperationexception( "only virtual properties supported." ); } } } // responsible instantiating dynamic // proxies of foo , derivatives. public static class mill { // static fields. static readonly proxygenerator proxygenerator = new proxygenerator(); static readonly waitforinitinterceptor interceptor = new waitforinitinterceptor(); // mill method. public static t create<t>() t : foo { homecoming proxygenerator.createclassproxy<t>(interceptor); } class waitforinitinterceptor : iinterceptor { public void intercept(iinvocation invocation) { // applies getters only. if (invocation.method.name.startswith("get_")) { var foo = invocation.invocationtarget foo; if (foo != null) { // block until initialisation completes. task.whenall(foo.initialisationtasks).wait(); } // go on target method. invocation.proceed(); } } } } }

now if create class fooderived : foo has public constructor or non-virtual properties (properties don't have getter exempt rule), base of operations constructor throw thereby forcing consumer utilize foo.factory.create<fooderived>().

if fooderived needs perform own asynchronous initialisation work, can add together own tasks initialisationtasks - property getter block until all of them have completed.

this code bit rough due each 'foo' proxy initialisation doing lot of intensive work behind covers (via validate). in ideal world have kind of cache (perhaps dictionary) of types have passed validation, , skip slow reflection-bound validation those.

alternative approach

while dynamic proxies fun, design flawed. concerns not well-separated. foo shouldn't worrying pulling out own info in first place, , shouldn't worrying tasks, thread pool , like. discussed extensively in comments, , think best bet kick off info loading tasks @ point have plenty info so, save task references (or whatever other async unit of work you're using), , await them (or block getting result or calling wait) when need utilize fully-loaded instance. ensures foo instances not accessible until loading finished , gives reasonable command on how async object loading scheduled. could, example, roll own limited concurrency scheduler, or utilize concurrentexclusiveschedulerpairs exclusivescheduler ensure not flooding thread pool work. batching object loading (using task<ienumerable<foo>> instead of ienumerable<task<foo>>, example) way of keeping tabs on number of tasks create. it's easy creative async loading 1 time decouple object construction logic, , it's right way go.

c# reflection getter-setter

No comments:

Post a Comment