在本地函数callback线程上运行asynchronous任务继续

我有一个C函数FsReadStream做一些asynchronous工作,并采取callback。 完成后,使用QueueUserWorkItem窗口函数调用callback。

我想从托pipe代码(C#)使用asynchronous/等待模式调用此function。 所以我做了以下

  1. 构造一个传递构造函数的Task对象返回结果的lambda。
  2. 构build使用RunSynchronously方法运行此任务的callback
  3. 调用asynchronous本地函数,传入callback
  4. 将任务对象返回给调用者

我的代码看起来像这样

 /// Reads into the buffer as many bytes as the buffer size public Task<ReadResult> ReadAsync(byte[] buffer) { GCHandle pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); IntPtr bytesToRead = Marshal.AllocHGlobal(sizeof(long)); Marshal.WriteInt64(bytesToRead, buffer.Length); FsAsyncInfo asyncInfo = new FsAsyncInfo(); ReadResult readResult = new ReadResult(); Task<ReadResult> readCompletionTask = new Task<ReadResult>(() => { return readResult; }); TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext(); asyncInfo.Callback = (int status) => { readResult.ErrorCode = status; readResult.BytesRead = (int)Marshal.ReadInt64(bytesToRead); readCompletionTask.RunSynchronously(scheduler); pinnedBuffer.Free(); Marshal.FreeHGlobal(bytesToRead); }; // Call asynchronous native method NativeMethods.FsReadStream( pinnedBuffer.AddrOfPinnedObject(), bytesToRead, ref asyncInfo); return readCompletionTask; } 

我这样称呼

 ReadResult readResult = await ReadAsync(data); 

我有两个问题

  1. 如何使调用之后运行的代码await ReadAsync在与callback相同的线程上运行? 目前,我看到它运行在不同的线程,即使我调用readCompletionTask.RunSynchronously 。 我在ASP.NET和IIS下运行这个代码。
  2. 本地QueueUserWorkItem函数是否使用与托pipe的ThreadPool.QueueUserWorkItem方法相同的线程池 ? 我的意见是,它应该,因此它应该是可能的托pipeTaskScheduler调度本地callback线程上的任务。

如何使调用之后运行的代码等待ReadAsync在与回调相同的线程上运行?

这是不可能的可靠的方式。 ExecuteSynchronously不是一个保证。 RunSynchronously也不能保证它。 您当然可以传入回调并同步调用该回调。

另外, FromCurrentSynchronizationContext返回什么? 我的蜘蛛感觉告诉我,这是基于一个误解。

本地QueueUserWorkItem函数是否使用与托管的ThreadPool.QueueUserWorkItem方法相同的线程池?

我不这么认为,即使是这样的情况下,你不能针对特定的线程。 你只能瞄准一个特定的游泳池。

为什么你需要在同一个线程上执行? 通常,那些询问这个问题的人确实想要和需要别的东西。


你创建和返回任务的方式很奇怪。 为什么不使用基于TaskCompletionSource的标准模式?


我认为你有一个GC洞,因为没有保持asyncInfo.Callback活着。 当本地电话正在进行时,它可以被收集起来。 在回调中使用GC.KeepAlive

你不应该使用现代代码中的Task构造函数。 完全一样。 永远。 没有用例。

在这种情况下,您应该使用TaskCompletionSource<T>

如何使调用之后运行的代码等待ReadAsync在与回调相同的线程上运行?

你不能保证; await就是不行。 如果代码绝对必须在同一个线程上执行,那么它应该直接从回调中调用。

但是,如果只是在同一个线程上执行,那么你不必做任何特殊的事情; await已经使用ExecuteSynchronously标志:

 public Task<ReadResult> ReadAsync(byte[] buffer) { var tcs = new TaskCompletionSource<ReadResult>(); GCHandle pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); IntPtr bytesToRead = Marshal.AllocHGlobal(sizeof(long)); Marshal.WriteInt64(bytesToRead, buffer.Length); FsAsyncInfo asyncInfo = new FsAsyncInfo(); asyncInfo.Callback = (int status) => { tcs.TrySetResult(new ReadResult { ErrorCode = status; BytesRead = (int)Marshal.ReadInt64(bytesToRead); }); pinnedBuffer.Free(); Marshal.FreeHGlobal(bytesToRead); }; NativeMethods.FsReadStream(pinnedBuffer.AddrOfPinnedObject(), bytesToRead, ref asyncInfo); return tcs.Task; } 

本地QueueUserWorkItem函数是否使用与托管的ThreadPool.QueueUserWorkItem方法相同的线程池?

不,那是两个完全不同的线程池。