Task.Factory.StartNew + TaskCreationOptions.龙润解释 &



我正在努力理解David Fowler在这里所说的Task.Factory.StartNew+TaskCreationOptions.LongRunning

💡注意:不要使用TaskCreationOptions。使用异步代码创建一个新线程,该线程将在第一次等待后被销毁。

我知道在这种情况下没有Task.RunTask.Factory.StartNew的意义,因为SendLoopAsync和ReceiveLoopAsync是完全异步的。我还知道,如果在这些方法中的任何一个中有一个耗时的同步部分,那么Task.Run/Task.Factory。StartNew应该在这个方法里面

大卫·福勒在他的声明中是什么意思?在异步任务中不应该有TaskCreationOptions.LongRunning?或者他的意思是SendLoopAsync/ReceiveLoopAsync不应该是异步的?我还知道TaskCreationOptions。LongRunning意味着任务将立即启动,这与调度器调度的普通任务不同,它可能需要一些时间才能结束。当同时启动多个连接时,您可以注意到这种行为,这会导致Send和Receive循环以明显的延迟启动。

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

David Fowler的意思是SendLoopAsync/ReceiveLoopAsync不应该是异步的。如果任务要使用启动线程的持续时间以纳秒为单位,那么以LongRunning启动任务是没有意义的。ThreadPool的发明正是为了处理这些类型的情况。如果ThreadPool因为饱和而没有足够的响应,那么更合乎逻辑的做法是尝试找到饱和的原因并修复它,而不是绕过ThreadPool,并在每次有一些微秒值的工作要做时创建新线程。

下面是LongRunning与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($"{title } after {elapsed,3} msec worker thread is {state}");

输出:

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毫秒对于如此小的工作量来说就像是一个时间。这6毫秒最有可能用于线程间通信,以及线程的创建和销毁。

最新更新