长时间运行的任务 vs. 线程 -- 性能

5
假设我有一些长时间运行的后台作业。每个作业都会执行一些工作,然后获取下一个作业并运行,一直执行到结束。
当前使用任务来实现。我有一个JobStream,它在循环中一次运行一个作业。根据负载情况,我可以同时运行5、15或50个这样的流。 作业管理器
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);
    });
}

我的问题是,任务是否在这里是一个好的选择,或者我应该按照旧有的方式创建线程?我主要关心的是性能和确保作业可以独立运行,而不会因为其他作业占用太多资源而被阻塞。


你真的应该使用微软的响应式框架(NuGet“System.Reactive”)来完成这个任务。它更加强大和简单。 - Enigmativity
我会去查看的,谢谢。 - Josh M.
@JoshM.,这让我想起了这个问题 - noseratio - open to work
1
另外你可以尝试使用TPL Dataflow - VMAtm
2个回答

7

对于这种情况,您真的应该使用微软的响应式框架(NuGet“System.Reactive”)。它更加强大和简单。

以下是一个示例:

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;
}

这会同时运行10个流并在每个流中调用int nextJob(int streamNumber)。我模拟了每个工作10秒钟的时间,但输出每秒产生一次结果。

这个查询会在10个流上无限重复,直到调用subscription.Dispose()停止所有操作。


感谢您的回答。不过我仍然对在这种情况下任务和线程有何区别感到好奇。 - Josh M.
我认为你可以用 SelectMany 代替 Select -> Merge - bradgonesurfing
1
@bradgonesurfing - 是的,但我认为这样更清晰。 - Enigmativity
也许你是对的。我倾向于使用SelectMany,但它确实有一种神奇的氛围,使得代码有点难以理解 :) - bradgonesurfing

3

@Enigmativity提供的答案很好。

但是关于作业和线程之间的性能差异:

如果作业运行时间长且需要大量CPU计算,那么性能差异可以忽略不计。

如果作业运行时间长但不需要大量CPU计算,请使用任务(tasks),因为这样更方便且节省创建线程的成本。

如果作业运行时间短,请使用自己的队列和传统的多线程方式,因为TPL(任务并行库)会对短时间运行的作业造成显著的开销。

与传统的多线程相比,任务是一种方便的后台作业运行方式。它确实节省了创建线程的成本,但只有当您需要创建大量(数千个)线程时才会产生影响。任务会增加一些排队和调度的开销,并跟踪结果和异常,但只有当您创建大量(数十万个)任务时才会产生影响。如果作业确实是长时间运行的,则这些都不是问题。如果您需要处理如此多的长时间运行的作业,那么您需要担心的问题就不是比较任务和线程之间的性能差异了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接