task.factory.startnew+ taskcreationoptions.longrunning解释

发布于 2025-01-22 04:24:07 字数 160 浏览 2 评论 0 原文

I'm trying to understand what David Fowler said about Task.Factory.StartNew + TaskCreationOptions.LongRunning

I'm trying to understand what David Fowler said about Task.Factory.StartNew + TaskCreationOptions.LongRunning here.

???? NOTE: Don't use TaskCreationOptions.LongRunning with async code as this will create a new thread which will be destroyed after first await.

I know that there is no point of having Task.Run or Task.Factory.StartNew in this case because SendLoopAsync and ReceiveLoopAsync are completely async. I also know that if there is a time-consuming synchronous part inside either one of these methods, the Task.Run/Task.Factory.StartNew should be inside that method.

What does David Fowler mean in his statement? That there shouldn't be TaskCreationOptions.LongRunning from within an async task? Or he meant that SendLoopAsync/ReceiveLoopAsync should not be async? I also know that TaskCreationOptions.LongRunning means that the task will start immediately, which isn't the case with just a normal task which gets scheduled by the scheduler, and might take some time to wind up. You can notice this behavior when starting multiple connections concurrently, which caused the Send and Receive loop to start with a significant delay.

public async Task StartAsync(CancellationToken cancellationToken)
{
    _ = Task.Factory.StartNew(_ => SendLoopAsync(cancellationToken), TaskCreationOptions.LongRunning, cancellationToken);
    _ = Task.Factory.StartNew(_ => ReceiveLoopAsync(cancellationToken), TaskCreationOptions.LongRunning, cancellationToken);
}

private async Task SendLoopAsync()
{
    await foreach (var message in _outputChannel.Reader.ReadAllAsync(_cancellationSource?.Token))
    {
        if (_clientWebSocket.State == WebSocketState.Open)
        {
            await _clientWebSocket.SendAsync(message.Data.AsMemory(), message.MessageType, true, CancellationToken.None).ConfigureAwait(false);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

谈情不如逗狗 2025-01-29 04:24:08

大卫·福勒=“ nofollow noreferrer”>表示 sendloopasync / aceiveloopAsync 不应是异步。 ,如果此任务将在纳秒内测量的持续时间内使用起始线程。 发明是为了准确处理这些类型的情况。如果 threadpool 由于已经饱和而响应不足,那么尝试找到饱和的原因并修复它是更合理的,而不是绕过 thread> threadpool ,并且每次您要做的工作值得一秒钟时,都会创建新线程。

这是 longRunning 与异步:

Stopwatch stopwatch = Stopwatch.StartNew();
Thread workerThread = null;
ConcurrentQueue<(string, long, System.Threading.ThreadState)> entries = new();
Task<Task> taskTask = Task.Factory.StartNew(async () =>
{
    workerThread = Thread.CurrentThread;
    entries.Enqueue(("A", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
    await Task.Delay(500);
    entries.Enqueue(("D", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
}, default, TaskCreationOptions.LongRunning, TaskScheduler.Default);

taskTask.Wait();
entries.Enqueue(("B", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));

workerThread.Join();
entries.Enqueue(("C", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));

await taskTask.Unwrap();
entries.Enqueue(("E", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));

foreach (var (title, elapsed, state) in entries)
    Console.WriteLine($"{title } after {elapsed,3} msec worker thread is {state}");

output:

A after   2 msec worker thread is Background
B after   6 msec worker thread is Background, Stopped
C after   6 msec worker thread is Stopped
D after 507 msec worker thread is Stopped
E after 507 msec worker thread is Stopped

工人线的寿命最多是6毫秒。它真正要做的就是实例化异步状态机,并使用 system.threading.timer 组件安排回调。 6毫秒对我来说就像是一个微小工作量的eon。这6毫秒很可能用于线程间的交流以及线程的创建和破坏。

David Fowler means that the SendLoopAsync/ReceiveLoopAsync should not be async. There is no point at starting a task as LongRunning, if this task is going to use the starting thread for a duration measured in nanoseconds. The ThreadPool was invented in order to handle exactly these types of situations. In case the ThreadPool is not responsive enough because it has become saturated, then it's more logical to try to find the cause of the saturation and fix it, instead of bypassing the ThreadPool, and creating new threads every time you have some microseconds-worth of work to do.

Here is a demonstration of what happens when LongRunning is combined with async:

Stopwatch stopwatch = Stopwatch.StartNew();
Thread workerThread = null;
ConcurrentQueue<(string, long, System.Threading.ThreadState)> entries = new();
Task<Task> taskTask = Task.Factory.StartNew(async () =>
{
    workerThread = Thread.CurrentThread;
    entries.Enqueue(("A", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
    await Task.Delay(500);
    entries.Enqueue(("D", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
}, default, TaskCreationOptions.LongRunning, TaskScheduler.Default);

taskTask.Wait();
entries.Enqueue(("B", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));

workerThread.Join();
entries.Enqueue(("C", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));

await taskTask.Unwrap();
entries.Enqueue(("E", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));

foreach (var (title, elapsed, state) in entries)
    Console.WriteLine(
quot;{title } after {elapsed,3} msec worker thread is {state}");

Output:

A after   2 msec worker thread is Background
B after   6 msec worker thread is Background, Stopped
C after   6 msec worker thread is Stopped
D after 507 msec worker thread is Stopped
E after 507 msec worker thread is Stopped

Try it on Fiddle.

The lifetime of the worker thread was at most 6 milliseconds. All it really had to do was to instantiate an async state machine, and schedule a callback using a System.Threading.Timer component. 6 milliseconds look to me like an eon for such a minuscule workload. Most probably these 6 milliseconds were spent for inter-thread communication, and for the thread's creation and destruction.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文