我有一个 C 函数 FsReadStream
,它执行一些异步工作并接受回调。完成后,它使用 QueueUserWorkItem 调用回调窗口函数。
我正在尝试使用异步/等待模式从托管代码 (c#) 调用此函数。所以我做了以下
- 构造一个
Task
对象,向构造函数传递一个返回结果的 lambda。 - 使用
RunSynchronously
方法构造一个运行此任务的回调 - 调用异步原生函数,传入回调
- 返回任务对象给调用者
我的代码看起来像这样
/// 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);
我有两个问题
- 如何使调用
await ReadAsync
后运行的代码与回调在同一线程上运行?目前,我看到它在不同的线程上运行,即使我正在调用readCompletionTask.RunSynchronously
。我在 ASP.NET 和 IIS 下运行这段代码。 - 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/