我有一個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下運行此代碼。 - 原生
QueueUserWorkItem
函數是否使用與管理的ThreadPool.QueueUserWorkItem方法相同的線程池?我的意見是,它應該,因此管理的TaskScheduler
應該可以在本地回調線程上安排任務。
我使用'Task' ctor而不是'TaskCompletionSource',因爲這允許我在運行任務時指定調度器。調度程序的類型爲「System.Threading.Tasks.SynchronizationContextTaskScheduler」。感謝您指出GC孔。 – tcb
嘗試在回調線程上等待後執行代碼,以避免線程之間的上下文切換 – tcb
您可以爲任務指定調度程序,但不爲延續指定調度程序。在任何特定的線程上運行'return readResult;'沒有意義。 '避免上下文切換'好吧,所以爲continuations指定'ExecuteSynchronously'。這工作99%的時間。然後,扔掉時髦的TCS仿真並使用TCS。這是否回答這個問題? – usr