C# 在简单的自定义同步上下文上使用async、await的线程峰值数
我编写了一个简单的同步上下文,它什么也不做,只是将要在同一线程上执行的任务排队,一次一个 但是,当它遇到等待时,似乎会触发一个新的线程。 线程数增加了一倍。我很难理解发生了什么。这不应该只运行3个线程吗(1个线程用于应用程序,另外2个线程用于GC、JIT) 但它立即飙升至6 更新:在x64发行版上运行,在具有2核-4线程的Intel Core i5上运行。 Microsoft.NET 4.5 SDK,已启用优化C# 在简单的自定义同步上下文上使用async、await的线程峰值数,c#,.net,multithreading,task-parallel-library,async-await,C#,.net,Multithreading,Task Parallel Library,Async Await,我编写了一个简单的同步上下文,它什么也不做,只是将要在同一线程上执行的任务排队,一次一个 但是,当它遇到等待时,似乎会触发一个新的线程。 线程数增加了一倍。我很难理解发生了什么。这不应该只运行3个线程吗(1个线程用于应用程序,另外2个线程用于GC、JIT) 但它立即飙升至6 更新:在x64发行版上运行,在具有2核-4线程的Intel Core i5上运行。 Microsoft.NET 4.5 SDK,已启用优化 using System; using System.Collections.Gen
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
using System.Diagnostics;
using System.Threading;
class Program
{
static void Main(string[] args)
{
var ctx = new SyncContext();
SynchronizationContext.SetSynchronizationContext(ctx);
ctx.Post(o => Console.WriteLine("Hello"), null);
ctx.Post(
async o =>
{
Console.WriteLine("Entered async lambda.");
Console.ReadLine();
await Console.Out.WriteAsync("Async out.");
Console.WriteLine("Async post back.");
Console.ReadLine();
Console.WriteLine("End async.");
}, null);
ctx.Run();
}
}
public class SyncContext : SynchronizationContext
{
private readonly Queue<Action> messageQueue = new Queue<Action>();
private bool IsRunning { get; set; }
public void Stop()
{
IsRunning = false;
if (messageQueue.Count == 0)
{
lock (messageQueue)
{
Monitor.Pulse(messageQueue);
}
}
}
public void Run()
{
IsRunning = true;
Action queuedAction;
while (IsRunning)
{
lock (messageQueue)
{
if (messageQueue.Count == 0)
{
// Make sure it wasn't stopped while contending for the lock.
if (IsRunning)
{
Monitor.Wait(messageQueue);
}
if (!IsRunning)
{
break;
}
}
queuedAction = messageQueue.Dequeue();
}
queuedAction();
Console.WriteLine(Process.GetCurrentProcess().Threads.Count);
}
}
/// <summary>
/// When overridden in a derived class, dispatches an asynchronous message to a synchronization context.
/// </summary>
/// <param name="d">The <see cref="T:System.Threading.SendOrPostCallback" /> delegate to call.</param>
/// <param name="state">The object passed to the delegate.</param>
public override void Post(SendOrPostCallback d, object state)
{
lock (messageQueue)
{
messageQueue.Enqueue(() => d(state));
Monitor.Pulse(messageQueue);
}
}
}
}
你看到了什么输出?在使用VS2013和.net 4.5的机器上,我得到以下输出:
Hello
12
Entered async lambda.
Async out.Async post back.
End async.
12
在Linqpad中,我得到以下信息:
Hello
28
Entered async lambda.
Async out.Async post back.
End async.
28
因此,在这两种情况下,看起来都没有启动任何新线程。您看到的行为与同步上下文无关,如下面的代码所示。我认为原因是,当您启动异步任务时,您正在点击TPL任务调度程序 最可能的情况是,任务调度器(或其背后的其他TPL惰性初始化逻辑)根据自己的需要旋转多个线程(或者使用
ThreadPool
threads)。显然,这种逻辑足够聪明,不会对synchronos任务执行此操作,例如,如果您执行了wait Task.FromResult(true)
而不是wait Console.Out.WriteAsync()
下面是一个使用自定义等待器的示例:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static async Task TestAsync()
{
Console.WriteLine("A, threads: {0}, thread: {1}", Process.GetCurrentProcess().Threads.Count, Thread.CurrentThread.ManagedThreadId);
await new CustomAwaiter(async: false);
Console.WriteLine("B, threads: {0}, thread: {1}", Process.GetCurrentProcess().Threads.Count, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("C, threads: {0}, thread: {1}", Process.GetCurrentProcess().Threads.Count, Thread.CurrentThread.ManagedThreadId);
await new CustomAwaiter(async: true);
Console.WriteLine("D, threads: {0}, thread: {1}", Process.GetCurrentProcess().Threads.Count, Thread.CurrentThread.ManagedThreadId);
}
static void Main(string[] args)
{
TestAsync().Wait();
}
}
public struct CustomAwaiter:
System.Runtime.CompilerServices.INotifyCompletion
{
readonly bool _async;
public CustomAwaiter(bool async = true) { _async = async; }
// awaiter methods
public CustomAwaiter GetAwaiter() { return this; }
public bool IsCompleted {get { return !_async; } }
public void GetResult() { }
// ICriticalNotifyCompletion
public void OnCompleted(Action continuation) { continuation(); }
}
}
输出:
A, threads: 3, thread: 1
B, threads: 3, thread: 1
C, threads: 3, thread: 1
D, threads: 6, thread: 1
A、 线程:3,线程:1
B、 线程:3,线程:1
C、 线程:3,线程:1
D、 线程:6,线程:1
正如@StephenCleary所指出的,线程的数量确实不是您应该担心的问题。无论如何,你也不应该依赖这种行为。如果您对那里到底发生了什么感到好奇,请使用。Updated和环境详细信息深入了解第三方物流实施细节。抱歉重复编辑。你确定你没有运行调试构建,或者主机进程没有增加你的线程数吗?我不明白为什么这个简单的控制台应用程序应该运行那么多线程。我在调试中使用它,在发布模式中两次都返回了13个线程。我想,这是一个根据系统而变化的实现细节,正如Stephen Cleary评论的那样。在运行时内可能无法控制它。线程数(例如,线程池中的线程数)是一个实现细节,不需要担心。这里从不使用线程池。那么,当您显式地编程使用单个线程时,有没有办法确保池中没有启动额外的线程?还有,所有这些线程都是线程池线程?有任何文档可以理解这些额外线程是什么吗?没有单线程的.NET应用程序。运行时总是可以将工作排队到线程池,例如控制台I/O。 A, threads: 3, thread: 1 B, threads: 3, thread: 1 C, threads: 3, thread: 1 D, threads: 6, thread: 1