【发布时间】:2020-05-11 16:27:11
【问题描述】:
我正在使用一个非托管库,该库要求对其 API 的所有调用都在同一个线程上运行。我们想使用响应式扩展的EventLoopScheduler 来促进这一点,因为我们将使用 Observable 来处理其他事情。
我在下面的代码示例中使用类似于Run 方法的方法来执行调度程序中的代码,该调度程序将始终在同一线程上运行。当我使用托管代码时,它按预期工作,所有调用都在事件循环管理的线程上运行,并且异步调用之前/之后是主线程。
但是,当我调用 P/Invoke 时(代码示例中的只是一个示例,我并没有在我的代码中真正调用它,但行为是相同的),线程确实在事件上运行循环线程,但之后的一切都是如此!
我尝试添加ConfigureAwait(true)(和false),但它并没有改变任何东西。我真的对这种行为感到困惑,为什么调用 P/Invoke 会改变等待后继续的线程!!?
这是要重现的代码:
[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)
【问题讨论】:
-
有什么理由不使用 System.Threading 命名空间(例如使用自定义任务调度程序)? stackoverflow.com/questions/30719366/…
-
@SimonMourier 是的,首先它比创建事件循环要多得多:)。然后,我实际上一开始就尝试过,但我想不出一个好的解决方案来让它与我用来轮询其中一些 API 调用的 RX Observables 一起工作,这个解决方案更适合我正在做的其他事情。而且,对我来说更重要的是,即使我最终以不同的方式做事,我也想了解为什么这不起作用。它做了一些非常反直觉的事情,打破了我对 C# 中 async/await 的理解
-
控制台应用之所以特别,是因为它没有设置 SynchronizationContext:devblogs.microsoft.com/pfxteam/…。但是,我已经对 EventLoopScheduler 特定的测试做了一些测试,但它仍然无法正常工作。 pastebin.com/raw/Kh2Yw9pn 闻起来很糟糕。它适用于“常规”任务/TaskScheduler。
-
有趣。这是netfx还是netcore?我没有对 EventLoopScheduler 做任何事情,但我认为这个想法是它安装了一个自定义同步上下文,队列在初始线程上工作?
-
如果我有时间的话,我明天将不得不玩 Rx,但我的第一个猜测是 EventLoopScheduler 本身并没有设置同步上下文,而只是在 observable 内部使用。当您执行
await <observable>时,将使用当前同步上下文,这是控制台应用程序的默认上下文,与 EventLoopScheduler 无关,这意味着您将获得默认的“从线程池中选择线程”。那里有一些优化,很容易让你回到同一个线程进行同步代码。
标签: c# multithreading async-await pinvoke system.reactive