1

I am trying to do fiber-like code that I can get into task and get out of it. The code I tried:

class TaskActivity {
    CancellationTokenSource _m=new CancellationTokenSource( int.MaxValue )
        ,_t=new CancellationTokenSource( int.MaxValue );

    public async Task PauseTask( ) { //call and await it when I want to pause task inside itself
        _m.Cancel( );
        _t = new CancellationTokenSource( int.MaxValue );
        while( !_t.IsCancellationRequested )
            await Task.Yield( );
    }
    public async Task ResumeTask( ) { //call and wait for it when I want to resume a task from the main context
        _t.Cancel( );
        _m = new CancellationTokenSource( int.MaxValue );
        while( !_m.IsCancellationRequested )
            await Task.Yield( );
    }
}

It works well, but it consumes a lot of CPU when I call Thread.Sleep at the Task\Main context because it runs in the loop without stop at the other side. When I tried await Task.Delay( int.MaxValue, (_m\_t) ); instead of await Task.Yield( );, it didn't consumed a lot of CPU but instead it deadlocked sometimes because the Task doesn't yield to another Task.

My question is, how to fuse Task.Delay and Task.Yield so it doesn't consume a lot of CPU but still able let other Tasks work?

KugBuBu
  • 630
  • 5
  • 21
  • FYI: [A pattern to pause/resume an async task?](http://stackoverflow.com/q/19613444/1768303). – noseratio May 27 '14 at 01:57
  • @Noseratio Saw that, but I want to switch context between the main context and a Task instead of just Resume and Pause. – KugBuBu May 27 '14 at 11:30
  • KugBuBu, if you mean cooperative execution (like co-routines), you may want to check [this](http://stackoverflow.com/a/22085087/1768303). Also, [this](http://stackoverflow.com/q/22852251/1768303) and [this](http://stackoverflow.com/a/23869010/1768303). Your scenario is most likely covered by one of these links. – noseratio May 27 '14 at 11:37
  • @Noseratio Well I am OK I just need that answerer to accept my edit, because his code had bug and I'll accept it. Thanks anyway! – KugBuBu May 27 '14 at 11:40
  • No worries, it's just that I don't think this is how you should switch between contexts, if you want to keep both tasks active. – noseratio May 27 '14 at 11:44
  • @Noseratio Well they ain't tasks, it's main context (A normal function) and just one Task that I want to switch between them. – KugBuBu May 27 '14 at 14:39

1 Answers1

6

Instead of doing a while loop, you can Register with a CancellationToken to notify you when the source is flagged:

public Task PauseTask( ) 
{ 
    _m.Cancel( );
    _t = new CancellationTokenSource( int.MaxValue );

    // Make a task yourself
    var task = new TaskCompletionSource<bool>();
    _t.Token.Register(() => task.TrySetResult(true)); // Mark the task done
    return task.Task;
}

This avoids the effective spin wait you're using via while with Task.Yield, and directly marks the task completed when the cancellation occurs.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • I am terribly sorry for being beginner. When I switched the variables and putted the code to the `ResumeTask` it deadlocked. It switched to the `Task`, switched back to the main context, again to the `Task` and died. By the way in the main context I used `activity.ResumeTask( ).Wait( );`, it's OK? – KugBuBu May 26 '14 at 18:32
  • I can't call `await` on it because it's in the main context, it is a call from the `Main` method. – KugBuBu May 26 '14 at 18:49
  • I tried running two tasks and it deadlocks too. Need to open new question? I don't think it'll be suitable for comments. – KugBuBu May 26 '14 at 18:58