运行 本机函数回调线程上的异步任务继续
Run async task continuation on native function callback thread
我有一个 C 函数 FsReadStream
,它执行一些异步工作并接受回调。完成后,它使用 QueueUserWorkItem windows 函数调用回调。
我正在尝试使用 async/await 模式从托管代码 (c#) 调用此函数。所以我做了以下
- 构造一个
Task
对象,向构造函数传递一个 returns 结果的 lambda。
- 使用
RunSynchronously
方法 构造一个 运行 执行此任务的回调
- 调用异步原生函数,传入回调
- Return调用者的任务对象
我的代码看起来像这样
/// 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
运行 之后在与回调相同的线程上生成 运行 的代码?目前,我在另一个线程上看到它 运行s,即使我正在调用 readCompletionTask.RunSynchronously
。我在 ASP.NET 和 IIS 下 运行ning 这段代码。
- 本机
QueueUserWorkItem
函数是否使用与托管 ThreadPool.QueueUserWorkItem 方法相同的线程池?我的意见是它应该,因此托管 TaskScheduler
应该可以在本机回调线程上安排任务。
How to make the code that runs after the call to await ReadAsync run on the same thread as the callback?
这不可能以可靠的方式进行。 ExecuteSynchronously
不是保证。 RunSynchronously
也不保证。您当然可以传入回调并同步调用该回调。
此外,FromCurrentSynchronizationContext
return 是什么?我的蜘蛛直觉告诉我这是一个误会...
Does the native QueueUserWorkItem function use the same threadpool as the managed ThreadPool.QueueUserWorkItem method?
我不这么认为,即使是这种情况,您也无法针对特定线程。您只能针对特定池。
为什么要在同一个线程上执行?通常,问这个问题的人确实想要并且需要别的东西。
您创建和return任务的方式很奇怪。为什么不使用基于 TaskCompletionSource
的标准模式?
我认为你有一个 GC 漏洞,因为没有什么能让 asyncInfo.Callback
存活。它可以在本机调用正在进行时被收集起来。在回调中使用 GC.KeepAlive
。
您不应在现代代码中使用 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 函数 FsReadStream
,它执行一些异步工作并接受回调。完成后,它使用 QueueUserWorkItem windows 函数调用回调。
我正在尝试使用 async/await 模式从托管代码 (c#) 调用此函数。所以我做了以下
- 构造一个
Task
对象,向构造函数传递一个 returns 结果的 lambda。 - 使用
RunSynchronously
方法 构造一个 运行 执行此任务的回调
- 调用异步原生函数,传入回调
- Return调用者的任务对象
我的代码看起来像这样
/// 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
运行 之后在与回调相同的线程上生成 运行 的代码?目前,我在另一个线程上看到它 运行s,即使我正在调用readCompletionTask.RunSynchronously
。我在 ASP.NET 和 IIS 下 运行ning 这段代码。 - 本机
QueueUserWorkItem
函数是否使用与托管 ThreadPool.QueueUserWorkItem 方法相同的线程池?我的意见是它应该,因此托管TaskScheduler
应该可以在本机回调线程上安排任务。
How to make the code that runs after the call to await ReadAsync run on the same thread as the callback?
这不可能以可靠的方式进行。 ExecuteSynchronously
不是保证。 RunSynchronously
也不保证。您当然可以传入回调并同步调用该回调。
此外,FromCurrentSynchronizationContext
return 是什么?我的蜘蛛直觉告诉我这是一个误会...
Does the native QueueUserWorkItem function use the same threadpool as the managed ThreadPool.QueueUserWorkItem method?
我不这么认为,即使是这种情况,您也无法针对特定线程。您只能针对特定池。
为什么要在同一个线程上执行?通常,问这个问题的人确实想要并且需要别的东西。
您创建和return任务的方式很奇怪。为什么不使用基于 TaskCompletionSource
的标准模式?
我认为你有一个 GC 漏洞,因为没有什么能让 asyncInfo.Callback
存活。它可以在本机调用正在进行时被收集起来。在回调中使用 GC.KeepAlive
。
您不应在现代代码中使用 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?
没有。那是两个完全不同的线程池。