c# - Unnecessary async/await when await is last? -
i've been dealing quite lot async await lately (read every possible article including stephen's , jon's last 2 chapters) , have come conclusion , don't know if it's 100% correct. - hence question .
since async
allows word await present , i'll leave async
aside.
afaiu , await continuation . instead of writing functional (continuational) code , write synchronous code. ( refer callback'able code)
so when compiler reaches await
- splits code 2 sections , registers second part executed after first part done ( i don't know why word callback
isn't used - done). ( meanwhile working - thread doing other things).
but looking @ code :
public async task processasync() { task<string> worktask = simulatework(); string st= await worktask; //do st } public task <string> simulatework() { return ... }
when thread reaches await worktask;
split method 2 sections . after simulatework
finished - continuation of method : aka : //do st
- executed.
all ok
but if method :
public async task processasync() { task<string> worktask = simulatework(); await worktask; //i don't care result , , don't have further commands }
here - don't need continuation , meaning - don't need await
split method means - don't need async /await here @ ! , still have same results/behaviour !
so :
public void processasync() { simulatework(); }
question:
- was 100% correct diagnostics ?
so, think await
below redundant, question's title implies:
public async task processasync() { task<string> worktask = simulatework(); await worktask; //i don't care result , , don't have further }
first of all, assume under "when await
last" mean "when await
await
". it's got that, because otherwise following not compile:
public async task processasync() { await task.delay(1000); task<string> worktask = simulatework(); return worktask; }
now, if it's only await
, can indeed optimize this:
public task processasync() { task<string> worktask = simulatework(); return worktask; }
however, give different exception propagation behavior, may have unexpected side effects. thing is, exceptions may thrown on caller's stack, depending on how simulatework
internally implemented. posted detailed explanation of behavior. never happens async
task
/task<>
methods, exception stored inside returned task
object. still may happen async void
method, that's different story.
so, if caller code ready such differences in exception propagation, may idea skip async/await
wherever can , return task
instead.
another matter if want issue fire-and-forget call. usually, still want track status of fired task somehow, @ least reason of handling task exceptions. not imagine case not care if task never completes, if logging.
so, fire-and-forget use helper async void
method stores pending task somewhere later observation, e.g.:
readonly object _synclock = new object(); readonly hashset<task> _pendingtasks = new hashset<task>(); async void queuetaskasync(task task) { // keep failed/cancelled tasks in list // observed outside lock (_synclock) _pendingtasks.add(task); try { await task; } catch { // not task's exception? if (!task.iscanceled && !task.isfaulted) throw; // re-throw // swallow, not remove faulted/cancelled task _pendingtasks // error observed later, when process _pendingtasks, // e.g.: await task.whenall(_pendingtasks.toarray()) return; } // remove completed task list lock (_synclock) _pendingtasks.remove(task); }
you'd call this:
public task processasync() { queuetaskasync(simulatework()); }
the goal throw fatal exceptions (e.g., out-of-memory) on current thread's synchronization context, while task result/error processing deferred until appropriate.
there's been interesting discussion of using tasks fire-and-forget here.
Comments
Post a Comment