none
How to use task_group to execute a task in the background. RRS feed

  • Question

  • Hi folks,

    (hopefully) A simple question:

    I am trying to use Concurrency::task_group::run() to run a task in the background. To elaborate:

    The task_group is a member of a class, so its lifetime is controlled by it. I call run from an Initialize() method of the class and I was hopping I could call task_group::wait from a RequestCancellationAndWait() method of the same class. 

    All good in paper; it looks, however, that task_group::wait() has to be called from the same function as ::run (is that correct?) so I am getting undefined behaviour.

    So, to the question: I there a way to implement the above using Concurrency::task_group?

    Many thanks,

    Yiannis

     

    PS: I don't think this will make any difference, but the call to task_group::run is made like this:

    			auto worker_task = make_task([&] {
    				pimpl_->database_worker_();
    			});
    
    			// Fire up worker
    			pimpl_->taskGroup_.run(worker_task);

    well, OK, here is the cancellation method as well:

    	void DBWorkQueue::CancelAndWait() {
    
    		// Only process if there is some work to cancel
    		if (pimpl_->state_initialized_) {
    			// request cancellation from the worker object
    			pimpl_->database_worker_.CancelAndWait();
    			// wait for the 'worker cancelled' event
    			pimpl_->worker_cancelled_.wait();
    
    			// reset the initialization flag
    			pimpl_->state_initialized_ = false;
    		}


    Monday, January 23, 2012 1:32 PM

Answers

  •  

    There are two different ways to schedule a task on a task_group:

     

    - You can create a task_handle and use the task_handle version of the run method. In this case, you are explicitly taking responsibility for the lifetime of the task_handle. In general, whoever supplies a task_handle object is responsible for guaranteeing that its lifetime contract is met. For run, this means that the task_handle object remains valid and does not destruct until the task is completed. Usually, this means that you must guarantee that wait or run_and_wait are called on the task_group prior to the destruction of the task_handle.

     

    - The easier way: you can call the functor based version of run: some_task_group.run([](){...}). Using this form, the runtime manages the task itself and you do not need to worry about lifetime management of task_handle objects. This is the typical form of calling if you are not using structured_task_group or doing other optimizations that involve wanting to directly manage allocation strategy and lifetime.

     

    In the example you show, you're probably better off with the functor form so as to not need to directly manage the lifetime of the task object.

     

    In any case, it is perfectly fine to call run and wait from different functions at different times -- even if you manage your own task allocations with task_handle objects -- so long as you are adhering to the lifetime contract.

    Tuesday, January 24, 2012 7:02 PM

All replies

  • Just to add to this:

    A member task_group is used in the async_future{}; implementation in this example:

    http://msdn.microsoft.com/en-us/library/dd764564.aspx

     

    It looks that task_group::wait() doesn't necessarily have to be called straight away after run and I am guessing that what messes things up for me is the automatic task_handle object's destructor.

     

    Is this correct? Is having task_handle as a member of a class where ::run() and ::wait()  are called at different times OK?

     

    Thanks,

    Yiannis

    Monday, January 23, 2012 2:38 PM
  •  

    There are two different ways to schedule a task on a task_group:

     

    - You can create a task_handle and use the task_handle version of the run method. In this case, you are explicitly taking responsibility for the lifetime of the task_handle. In general, whoever supplies a task_handle object is responsible for guaranteeing that its lifetime contract is met. For run, this means that the task_handle object remains valid and does not destruct until the task is completed. Usually, this means that you must guarantee that wait or run_and_wait are called on the task_group prior to the destruction of the task_handle.

     

    - The easier way: you can call the functor based version of run: some_task_group.run([](){...}). Using this form, the runtime manages the task itself and you do not need to worry about lifetime management of task_handle objects. This is the typical form of calling if you are not using structured_task_group or doing other optimizations that involve wanting to directly manage allocation strategy and lifetime.

     

    In the example you show, you're probably better off with the functor form so as to not need to directly manage the lifetime of the task object.

     

    In any case, it is perfectly fine to call run and wait from different functions at different times -- even if you manage your own task allocations with task_handle objects -- so long as you are adhering to the lifetime contract.

    Tuesday, January 24, 2012 7:02 PM
  • Thanks Bill,

    The functor form indeed works for me - good to know I can call run and wait on task_groups from different contexts.

    The project I am working on becomes a bit volatile every 1/10-15 runs (sigh...!) - I guess I'll have to look for the source elsewhere.

    Yiannis

     

    Tuesday, January 24, 2012 8:07 PM