在大规模的情况下,哪种方案更好--每个用户的循环任务和一个循环并为所有用户完成工作的单一任务?

问题描述 投票:0回答:1

我为每个用户创建了一个循环,它使用Task.Factory.StartNew()创建了一个轻量级任务。循环中的任务做了一个轻量级的工作,并睡眠了几秒钟。

如果有几千或几百万用户同时工作,这样的代码还能用吗?管理这么多线程会不会对服务器造成很大的负载?让一个线程为所有用户做这项工作是否更好?

这就是目前代码中的情况。

Task.Factory.StartNew(() =>
{
  // begin loop
  // synchronous web call
  // process and update result in DB
  // exit condition
  // sleep for few minutes
  // end loop
}
c# multithreading multitasking
1个回答
1
投票

你可以有几百万个长期运行的任务,但你不能有几百万个长期运行的线程(除非你拥有一台有TB内存的机器,因为每个线程都分配了 1 MB). 有这么多任务的方法是让它们成为async。而不是让它们与 Thread.Sleep你可以让他们 等待 异步a Task.Delay. 下面是一个例子。

var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task[] tasks = Enumerable.Range(1, 1_000_000).Select(index => Task.Run(async () =>
{
    await Task.Delay(index, ct); // Initial delay to spread things out
    while (true)
    {
        var webResult = await WebCallAsync(index, ct); // asynchronous web call
        await DbUpdateAsync(webResult, ct); // update result in DB
        await Task.Delay(1000 * 60 * 10, ct); // do nothing for 10 minutes
    }
})).ToArray();
Task.WaitAll(tasks);

目的是... CancellationTokenSource 是用于在任何时候通过调用 cts.Cancel(). 结合 Task.Delay 但取消会产生意想不到的开销,因为取消会通过 OperationCanceledException 异常,而100万个异常会给.NET基础架构造成相当大的压力。在我的PC中,开销大约是50秒100%的CPU消耗。如果你确实喜欢使用 CancellationTokens,一个变通的办法是使用一个替代的 Task.Delay 不抛出异常。这里是这个想法的一个实现。

/// <summary>Returns a <see cref="Task"/> that will complete with a result of true
/// if the specified number of milliseconds elapsed successfully, or false
/// if the cancellation token is canceled.</summary>
private static async Task<bool> NonThrowingDelay(int millisecondsDelay,
    CancellationToken cancellationToken = default)
{
    if (cancellationToken.IsCancellationRequested) return false;
    if (millisecondsDelay == 0) return true;
    var tcs = new TaskCompletionSource<bool>();
    using (cancellationToken.Register(() => tcs.TrySetResult(false)))
    using (new Timer(_ => tcs.TrySetResult(true), null, millisecondsDelay, Timeout.Infinite))
        return await tcs.Task.ConfigureAwait(false);
}

下面是如何使用 NonThrowingDelay 创建100万个可以立即取消的任务(几乎)的方法。

var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task[] tasks = Enumerable.Range(1, 1_000_000).Select(index => Task.Run(async () =>
{
    if (!await NonThrowingDelay(index, ct)) return; // Initial delay
    while (true)
    {
        var webResult = await WebCallAsync(index, ct); // asynchronous web call
        await DbUpdateAsync(webResult, ct); // update result in DB
        if (!await NonThrowingDelay(1000 * 60 * 10, ct)) break; // 10 minutes
    }
})).ToArray();
Task.WaitAll(tasks);
© www.soinside.com 2019 - 2024. All rights reserved.