简体   繁体   中英

Long-running task vs. threads — performance

Let's say I have some long running background jobs. Each job will do some work, then grab the next job and run it, and continue until the end of time.

This is currently implemented using Tasks. I have a JobStream which runs one job at a time within a loop. I could have 5, 15, or 50 of these streams running at once, depending on load.

JobManager

public Task Run(CancellationToken cancellationToken) {
    var jobTasks = Enumerable
        .Range(0, _config.BackgroundProcessor.MaximumSimultaneousJobs)
        .Select(o => JobStream.StartNew(..., () => RunNextJob(cancellationToken), cancellationToken));

    return Task.WhenAll(jobTasks);
}

JobStream

public static Task StartNew(Func<Task> nextJobRunner, CancellationToken cancellationToken) {
    var jobStream = new JobStream(nextJobRunner, cancellationToken);

    return jobStream.Start();
}

private Task Start() {
    return Task.Run(async () => {
        do {
            await _nextJobRunner();
        } while (!_cancellationToken.IsCancellationRequested);
    });
}

My question is, are tasks a good move here, or should I just create threads the old fashioned way? I'm mostly concerned about performance and ensuring that the jobs can run independently without getting tied up because another is grinding hard.

You really should be using Microsoft's Reactive Framework (NuGet "System.Reactive") for this. It's more powerful and simpler.

Here's an example:

void Main()
{
    int number_of_streams = 10;

    IObservable<int> query =
        Observable
            .Range(0, number_of_streams)
            .Select(stream_number =>
                Observable
                    .Defer(() => Observable.Start(() => nextJob(stream_number)))
                    .Repeat())
            .Merge();

    IDisposable subscription =
        query
            .Subscribe(x => Console.WriteLine(x));
}

public int nextJob(int streamNumber)
{
    Thread.Sleep(10000);
    return streamNumber;
}

This runs 10 simultaneous streams and calls int nextJob(int streamNumber) in each stream. I've simulated 10 seconds of work for each job, but the output produces a result every second.

This query repeats forever on the 10 streams until you call subscription.Dispose() and it'll all stop.

The answer provided by @Enigmativity is good.

But regarding performance differences between jobs and threads:

If the jobs are long running and CPU intensive, the performance differences are negligible.

If the jobs are long running but not CPU intensive, use tasks because it is convenient and saves the cost of creating threads.

If the jobs are short, use your own queue and old fashioned multi-threading because TPL overhead is significant for short running jobs.

Comparing to old fashioned multi-threading, task is a convenient way of running background jobs. It does save the cost of creating threads, but this cost only matters when you need to create a lot of(thousands) threads. Task does add some overhead for queuing and scheduling, and for tracking results and exceptions, but this only matters when you are creating a lot of them(hundreds of thousands). These won't be the case if the jobs are truly long running. If you need to process this many long running jobs, then you have a different problem to worry about than comparing performance difference between tasks and threads.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM