2

I'm looking to start using PPL in my application. (I'm currently using std::async)

I however have two (ugly) cases where I have to call long running functions that don't return any results. (Storing to database, and a network call). I don't wait for them to finish at the end of my main loop (as they are very slow), but simply continue on with the next iteration. I do this by spawning threads for these function calls, and detach the threads. I believe this is called fire-and-forget.

Is there a way to do fire-and-forget with PPL? Id like to avoid creating my own threads, and use void PPL tasks. Do PPL tasks block in the destructor?

Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
petke
  • 1,345
  • 10
  • 25
  • 2
    So, you will launch an unbounded number of network calls or database stores, never waiting for any to complete, never knowing if any complete, creating new threads and new threads and new threads? What, exactly, do you expect to happen at application shutdown? – Yakk - Adam Nevraumont Jun 09 '15 at 01:28
  • 1
    So, why not pick them up when they are done? You don't even have to pick them up right away: you can simply harvest the ones that are finished. At shutdown, you can wait a reasonable amount of time, and if they aren't going away you can log warnings/inform the user, and force a shut down anyhow. Fire-and-forget operations are fun to write, but either your framework should handle the cleanup (so it is fire-and-forget at the user end, but not in implementation), or you should handle it yourself in a non-toy application. – Yakk - Adam Nevraumont Jun 09 '15 at 16:45
  • 1
    For a practical problem, presume system load goes high, and a db call (maybe writing) isn't run for a loop. The next loop runs, and another db call queues up. It gets scheduled first. Now you have a hard to reproduce out-of-order pair of db writes causing database incoherence, with no trail leading back to what happened. What is worse is that this will only happen *rarely*, which means black-box QA will miss it, and even if you fix it if regressions occur black-box QA won't notice it either. Friends don't let friends launch threads willy-nilly. – Yakk - Adam Nevraumont Jun 09 '15 at 16:47
  • Fire-and-forget might be an ugly pattern, but sometimes its all you have. This is a real time app and waiting for these slow calls is impossible. The database stores take less time than one iteration of the main loop. So database stores and threads wont queue up. As in the old threads will normally finish running before new ones are created. The network call is even slower, but its also a rare call. That said, Id be interested in a better pattern. (I do a wait at shut down longer than any of these slow calls.) – petke Jun 09 '15 at 17:00
  • Thanks for the feedback. (I accidentally posted my comment before I had finished writing it, and couldn't edit it. So had to delete and repost. Sorry for any confusion.) – petke Jun 09 '15 at 17:04
  • 1
    Try fire-and-don't-forget. For things that should be ordered, a queue of tasks (say, db edits, which are far easier to reason about if ordered). For things that should be abandoned if restarted (say, pushing a current state to some reporter -- if a queue develops, finish the current task, then start the latest one). At shutdown, you still have these queues. You can even support a bounded number of parallel attempts. None of these needs extensive runtime costs. You can even get better runtime performance by reusing a worker thread, instead of launching a new thread each time. – Yakk - Adam Nevraumont Jun 09 '15 at 17:05
  • If I understand correctly ill have to have to store these long running tasks in a vector outside my main loop. Then wait on them at shut-down. (Also possibly cancel old queued up ones not yet running, every time I post a new task). Yes, I think that might work. Thanks. – petke Jun 09 '15 at 17:13
  • 1
    Ordered tasks can be scheduled as continuations. The continuations can then take cancellation token an if you want to cancel all the scheduled tasks you would just set the token to cancelled. You can keep the cancellation token sources used to create cancellation token in the main loop so that you can cancel tasks from the main loop. I wrote this post you might find interesting http://blog.3d-logic.com/2015/03/03/c-async-development-not-only-for-for-c-developers-part-iv-exception-handling/ - it's about exceptions (I have not gotten to cancellation yet) but cancellation works in a similar way. – Pawel Jun 12 '15 at 04:57
  • Thanks Pawel. I think I'll just go with two concurrency::task_group's (one for each type of call) outside the main loop. And just push (and cansel) tasks there from inside the main loop. Seems simple enough. – petke Jun 13 '15 at 01:15

0 Answers0