c# - 在 native 函数回调线程上运行异步任务继续

标签 c# windows async-await pinvoke

我有一个 C 函数 FsReadStream,它执行一些异步工作并接受回调。完成后,它使用 QueueUserWorkItem 调用回调窗口函数。

我正在尝试使用异步/等待模式从托管代码 (c#) 调用此函数。所以我做了以下

  1. 构造一个 Task 对象,向构造函数传递一个返回结果的 lambda。
  2. 使用 RunSynchronously 方法构造一个运行此任务的回调
  3. 调用异步原生函数,传入回调
  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 后运行的代码与回调在同一线程上运行?目前,我看到它在不同的线程上运行,即使我正在调用 readCompletionTask.RunSynchronously。我在 ASP.NET 和 IIS 下运行这段代码。
  2. native QueueUserWorkItem 函数是否使用与托管相同的线程池 ThreadPool.QueueUserWorkItem方法?我的意见是它应该,因此托管 TaskScheduler 应该可以在 native 回调线程上安排任务。

最佳答案

你不应该使用 Task现代代码中的构造函数。完全没有。曾经。没有用例。

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

How to make the code that runs after the call to await ReadAsync run on the same thread as the callback?

你不能保证; 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;
}

Does the native QueueUserWorkItem function use the same threadpool as the managed ThreadPool.QueueUserWorkItem method?

没有。这是两个完全不同的线程池。

关于c# - 在 native 函数回调线程上运行异步任务继续,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33353436/

相关文章:

windows - Windows 批处理文件可以确定自己的文件名吗?

mysql - NodeJS 异步/等待类 mysql/mariadb

c# - Task.WaitAll 在 ASP.NET 中挂起多个等待任务

c# - await AsyncMethod() 与 await await Task.Factory.StartNew<TResult>(AsyncMethod)

c# - 在文本框中从右到左输入?

c# - 如何阻止 EF 尝试更新 SQL Server 的计算列?

c# - 在 WPF Viewbox 中对齐内容

c# - 在 wpf c# 中单击更改按钮图像

windows - 如何解决 TPrintDialog 不保存设置?

windows - 批量格式化文件日期YYYYMMDD