Classic never-ending thread loop using tasks?

2020-07-23 05:47发布

问题:

Given is a very common threading scenario:

Declaration

private Thread _thread;
private bool _isRunning = false;

Start

_thread = new Thread(() => NeverEndingProc());
thread.Start();

Method

private void NeverEndingProc() {
    while(_isRunning) {
        do();
    }
}

Possibly used in a asynchronous tcp listener that awaits callbacks until it gets stopped by letting the thread run out (_isRunning = false).

Now I'm wondering: Is it possible to do the same thing with Task? Using a CancellationToken? Or are Tasks only for procedures that are expected to end and report status?

回答1:

You can certainly do this just by passing NeverEndingProc to Task.Run.

However, there is one important difference in functionality: if an exception is propagated out of NeverEndingProc in a bare Thread, it will crash the process. If it is in a Task, it will raise TaskScheduler.UnobservedException and then be silently ignored (as of .NET 4.5).

That said, there are alternatives you can explore. Reactive Extensions, for example, pretty much removes any need for the "infinite thread loop".



回答2:

One reason to use Task + CancellationToken is to make the individual processes and their cancellation more independent of each other. In your example, notice how NeverEndingProc needs a direct reference to the _isRunning field in the same class. Instead, you could accept an external token:

Start:

public void StartNeverEndingProc(CancellationToken token) {
    Task.Factory.StartNew(() => NeverEndingProc(token), token);
}

Method:

private void NeverEndingProc(CancellationToken token) {
    while (true) {
        token.ThrowIfCancellationRequested();
        do();
    }
}

Now cancellation is managed by the caller, and can be applied to multiple independent tasks:

var instance = new YourClass();

var cts = new CancellationTokenSource();

instance.StartNeverEndingProc(cts.Token);  // start your task
StartOtherProc(cts.Token);  // start another task

cts.Cancel();  // cancel both