我正在使用一个未管理的库,该库要求所有对其API的调用都在同一线程上运行。我们希望使用Reactive扩展的EventLoopScheduler
来实现这一点,因为我们将在其他地方使用Observable。
我正在使用类似下面代码示例中的Run
方法来在调度程序中执行代码,该方法将始终在同一线程上运行。当我使用托管代码时,这按预期工作,所有调用都在事件循环管理的线程上运行,并且异步调用之前/之后是主线程。
但是,当我调用P / Invoke(在代码示例中的函数仅用于举例,我并没有在我的代码中真正调用它,但行为是相同的)时,线程确实在事件循环线程上运行,但之后的所有操作也是如此!
我已经尝试添加ConfigureAwait(true)
(和false
),但没有改变任何内容。我对这种行为感到非常困惑,为什么调用P / Invoke会改变await之后继续运行的线程!!?
以下是重现代码:
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
public static Task Run(Action action, IScheduler scheduler)
{
return Observable.Start(action, scheduler).SingleAsync().ToTask();
}
public static string ThreadInfo() =>
$"\"{Thread.CurrentThread.Name}\" ({Thread.CurrentThread.ManagedThreadId})";
private static async Task Main(string[] args)
{
var scheduler = new EventLoopScheduler();
Console.WriteLine($"Before managed call on thread {ThreadInfo()}");
await Run(() => Console.WriteLine($"Managed call on thread {ThreadInfo()}"), scheduler);
Console.WriteLine($"After managed call on thread {ThreadInfo()}");
Console.WriteLine($"Before PInvoke on thread {ThreadInfo()}");
await Run(() => MessageBox(IntPtr.Zero, $"Running on thread {ThreadInfo()}", "Attention", 0), scheduler);
Console.WriteLine($"After PInvoke on thread {ThreadInfo()}");
}
执行后会返回类似以下的结果:
Before managed call on thread "" (1)
Managed call on thread "Event Loop 1" (6)
After managed call on thread "" (1)
Before PInvoke on thread "" (1)
Message box displayed with text: Running on thread "Event Loop 1" (6)
After PInvoke on thread "Event Loop 1" (6)
我原本期望
Before managed call on thread "" (1)
Managed call on thread "Event Loop 1" (6)
After managed call on thread "" (1)
Before PInvoke on thread "" (1)
Message box displayed with text: Running on thread "Event Loop 1" (6)
After PInvoke on thread "" (1)
await <observable>
时,当前的同步上下文被使用,这是控制台应用程序的默认上下文,与 EventLoopScheduler 没有任何关系,这意味着你会得到默认的“从线程池中选择线程”的效果。其中有一些优化可以很容易地导致你回到同步代码的同一线程。 - VooRun
方法中传递的工作确实在EventLoopScheduler
创建的线程中运行,但 await 后的继续也在该线程上,这在异步/等待上下文中是没有意义的。 - Gimly