c# - How to check if previous task is still running and stop/cancel it? -
i have background task run in next way.
this in attached behavior on textbox text changed event.
what if text changed , changed again, on sec alter check if previous task still running, if so, stop , go on latest one.
public class findtextchangedbehavior : behavior<textbox> { protected override void onattached() { base.onattached(); associatedobject.textchanged += ontextchanged; } protected override void ondetaching() { associatedobject.textchanged -= ontextchanged; base.ondetaching(); } private void ontextchanged(object sender, textchangedeventargs args) { var textbox = (sender textbox); if (textbox != null) { task.factory.startnew(() => { //do text search on object properties within datagrid //and populate temporary observablecollection items. classproptextsearch.init(itemtype, columnboundproperties); if (itemssource != null) { foreach (object o in itemssource) { if (classproptextsearch.match(o, searchvalue)) { tempitems.add(o); } } } //copy temporary collection ui bound observablecollection //on ui thread application.current.dispatcher.invoke(new action(() => myclass.instance.searchmarkers = tempitems)); }); } } [edit] have not tested yet mockup of might be.
cancellationtokensource cancellationtokensource = new cancellationtokensource(); private void ontextchanged(object sender, textchangedeventargs args) { var newcts = new cancellationtokensource(); var oldcts = interlocked.exchange(ref this.cancellationtokensource, newcts); if (oldcts != null) { oldcts.cancel(); } var cancellationtoken = newcts.token; var textbox = (sender textbox); if (textbox != null) { observablecollection<object> tempitems = new observablecollection<object>(); var ui = taskscheduler.fromcurrentsynchronizationcontext(); var search = task.factory.startnew(() => { classproptextsearch.init(itemtype, columnboundproperties); if (itemssource != null) { foreach (object o in itemssource) { cancellationtoken.throwifcancellationrequested(); if (classproptextsearch.match(o, searchvalue)) { tempitems.add(o); } } } }, cancellationtoken); //still considered. //if gets here , still updating ui //what do, upon searchmarkers beingness set below cancel //or wait until done , go on update again??? var displaysearchresults = search.continuewith(resulttask => myclass.instance.searchmarkers = tempitems, cancellationtoken.none, taskcontinuationoptions.onlyonrantocompletion, ui); } }
i bit worried proposing trawling through "object properties within datagrid" on non-ui thread - may work you're not setting values background thread, has bit of smell it.
ignoring now, allow me propose next solution:
private readonly semaphoreslim mutex = new semaphoreslim(1, 1); private cancellationtokensource cancellationtokensource; private void ontextchanged(object sender, textchangedeventargs args) { var newcts = new cancellationtokensource(); var oldcts = interlocked.exchange(ref this.cancellationtokensource, newcts); if (oldcts != null) { oldcts.cancel(); } var cancellationtoken = newcts.token; var textbox = (sender textbox); if (textbox != null) { // capturing // taskscheduler.fromcurrentsynchronizationcontext() // here , scheduling continuation using (ui) scheduler. task.factory.startnew(() => { // ensure 1 thread can execute // seek body @ given time. this.mutex.wait(cancellationtoken); seek { cancellationtoken.throwifcancellationrequested(); runsearch(cancellationtoken); cancellationtoken.throwifcancellationrequested(); //copy temporary collection ui bound observablecollection //on ui thread application.current.dispatcher.invoke(new action(() => myclass.instance.searchmarkers = tempitems)); } { this.mutex.release(); } }, cancellationtoken); } } edit
since know you're targeting async-aware framework, above solution can both simplified , enhanced.
i had create numerous assumptions how "grid properties" harvested , made effort decouple process (which in mind should running on dispatcher thread) actual search (which i'm scheduling on thread pool).
public class findtextchangedbehavior : behavior<textbox> { protected override void onattached() { base.onattached(); associatedobject.textchanged += ontextchanged; } protected override void ondetaching() { associatedobject.textchanged -= ontextchanged; base.ondetaching(); } private cancellationtokensource cancellationtokensource; // we're ui handler, hence async void. private async void ontextchanged(object sender, textchangedeventargs args) { // assume runs on ui thread: // no thread safety when exchanging cts. if (this.cancellationtokensource != null) { this.cancellationtokensource.cancel(); } this.cancellationtokensource = new cancellationtokensource(); var cancellationtoken = this.cancellationtokensource.token; var textbox = (sender textbox); if (textbox != null) { seek { // if async work completes quickly, // dispatcher flooded ui // update requests producing laggy user // experience. we'll around // introducing slight delay (throttling) // before going ahead , performing work. await task.delay(timespan.frommilliseconds(100), cancellationtoken); // cut down taskcanceledexceptions. // async void, we'll // exit method instead of throwing. // important: in order guarantee async // requests executed in right order // , respond cancellation appropriately, // need perform check after every await. // reason no longer need semaphore. if (cancellationtoken.iscancellationrequested) return; // harvest object properties within datagrid. // we're still on ui thread, // right place so. ienumerable<gridproperty> interestingproperties = .getinterestingproperties() .toarray(); // redundant if getinterestingproperties returns // list, array or similar materialised ienumerable. // appears cpu-bound, task.run appropriate. observablecollection<object> tempitems = await task.run( () => this.resolvesearchmarkers(interestingproperties, cancellationtoken) ); // not forget this. if (cancellationtoken.iscancellationrequested) return; // we've run completion meaning // ontextchanged has not been called again. // time update ui. myclass.instance.searchmarkers = tempitems; } grab (operationcanceledexception) { // expected. // can still thrown task.delay example. } grab (exception ex) { // really, unexpected exception. // makes sense: log it, invalidate // state, tear things downwards if necessary. } } } private ienumerable<gridproperty> getinterestingproperties() { // sure homecoming materialised ienumerable, // i.e. array, list, collection. throw new notimplementedexception(); } private observablecollection<object> resolvesearchmarkersasync( ienumerable<gridproperty> interestingproperties, cancellationtoken cancellationtoken) { var tempitems = new observablecollection<object>(); //do text search on object properties within datagrid //and populate temporary observablecollection items. foreach (var o in interestingproperties) { cancellationtoken.throwifcancellationrequested(); if (classproptextsearch.match(o, searchvalue)) { tempitems.add(o); } } homecoming tempitems; } } c# taskfactory
No comments:
Post a Comment