c# - Faulted vs Canceled task status after CancellationToken.ThrowIfCancellationRequested -
c# - Faulted vs Canceled task status after CancellationToken.ThrowIfCancellationRequested -
usually don't post question answer, time i'd attract attending think might obscure yet mutual issue. triggered this question, since reviewed own old code , found of affected this, too.
the code below starts , awaits 2 tasks, task1 , task2, identical. task1 different task2 in runs never-ending loop. imo, both cases quite typical real-life scenarios performing cpu-bound work.
using system; using system.threading; using system.threading.tasks; namespace consoleapplication { public class programme { static async task testasync() { var ct = new cancellationtokensource(millisecondsdelay: 1000); var token = ct.token; // start task1 var task1 = task.run(() => { (var = 0; ; i++) { thread.sleep(i); // simulate work item #i token.throwifcancellationrequested(); } }); // start task2 var task2 = task.run(() => { (var = 0; < 1000; i++) { thread.sleep(i); // simulate work item #i token.throwifcancellationrequested(); } }); // await task1 seek { await task1; } grab (exception ex) { console.writeline(new { task = "task1", ex.message, task1.status }); } // await task2 seek { await task2; } grab (exception ex) { console.writeline(new { task = "task2", ex.message, task2.status }); } } public static void main(string[] args) { testasync().wait(); console.writeline("enter exit..."); console.readline(); } } } the fiddle is here. output:
{ task = task1, message = operation canceled., status = canceled } { task = task2, message = operation canceled., status = faulted }why status of task1 cancelled, status of task2 faulted? note, in both cases not pass token 2nd parameter task.run.
there 2 problems here. first, it's thought pass cancellationtoken task.run api, besides making available task's lambda. doing associates token task , vital right propagation of cancellation triggered token.throwifcancellationrequested.
this doesn't explain why cancellation status task1 still gets propagated correctly (task1.status == taskstatus.canceled), while doesn't task2 (task2.status == taskstatus.faulted).
now, might 1 of rare cases clever c# type inference logic can play against developer's will. it's discussed in great details here , here. sum up, in case task1, next override of task.run inferred compiler:
public static task run(func<task> function) rather than:
public static task run(action action) that's because task1 lambda has no natural code path out of for loop, may func<task> lambda, despite not async , doesn't homecoming anything. alternative compiler favors more action. then, utilize of such override of task.run equivalent this:
var task1 = task.factory.startnew(new func<task>(() => { (var = 0; ; i++) { thread.sleep(i); // simulate work item #i token.throwifcancellationrequested(); } })).unwrap(); a nested task of type task<task> returned task.factory.startnew, gets unwrapped task unwrap(). task.run is smart enough such unwrapping automatically when accepts func<task>. the unwrapped promise-style task correctly propagates cancellation status inner task, thrown operationcanceledexception exception func<task> lambda. doesn't happen task2, accepts action lambda , doesn't create inner tasks. cancellation doesn't propagated task2, because token has not been associated task2 via task.run.
in end, may desired behavior task1 (certainly not task2), don't want create nested tasks behind scene in either case. moreover, behavior task1 may broken introducing conditional break out of for loop.
the right code task1 should this:
var task1 = task.run(new action(() => { (var = 0; ; i++) { thread.sleep(i); // simulate work item #i token.throwifcancellationrequested(); } }), token); c# .net multithreading task-parallel-library async-await
Comments
Post a Comment