使用SemaphoreSlim阻塞主线程,直到后台事件到达



是的,我知道,有很多关于这个话题的帖子。我读了很多,并经常(或多或少)成功地使用它们。现在我得到了一个旧的DLL(在。net 4.0中编程),那就是使用BackgroundWorkers来触发结果事件。无论我试图停下来等待这样的结果,似乎都没有达到目的。但也许你们中的一些人有我还没有尝试过的想法。

我在线程1中注册应答事件(根据Thread.CurrentThread.ManagedThreadId),调用连接方法并使用SemaphoreSlim等待应答。但在每一个星座,我得到回答事件后,超时发生。事件在线程3上,当我试图在一个特殊的Thread.Run(() => ...);上提高SemaphoreSlimAwaitAsync()(也有Await)时,它在线程id 8上。但是3号线程总是在超时之后出现。

private void ConnectDevice()
{
MobileDevice.DeviceConnected += new DeviceConnectedHandler(MobileDevice_Connected);
...
mSignal = new SemaphoreSlim(0, 1);
MobileDevice.Connect();
// Task.Run(() => MobileDevice.Connect());
int i = Thread.CurrentThread.ManagedThreadId;
var task = Task.Run(() => mSignal.WaitAsync(new TimeSpan(0, 0, cTimeout)).GetAwaiter().GetResult());
// Task<bool> task = Task.Run(async () => await WaitForSemaphore());
// var result = task.Wait();
IsConnected = task.Result;
// WaitForSemaphore();
...
}
private async Task<bool> WaitForSemaphore()
{
int j = Thread.CurrentThread.ManagedThreadId;
if (!await mSignal.WaitAsync(new TimeSpan(0, 0, 5)))
{
throw new MobileDeviceException("Device timed out");
}
return true;
}
//private void WaitForSemaphore()
//{
//    if (!mSignal.Wait(new TimeSpan(0, 0, cTimeout)))
//    {
//        throw new MobileDeviceException("Device timed out");
//    }
//}
private void MobileDevice_Connected(object sender, DeviceConnectedEventArgs e)
{
int k = Thread.CurrentThread.ManagedThreadId;
mSignal?.Release();
...
}

是的,我知道这很混乱。但我想告诉你,我已经尽力了。我又试了很多,但也删除了一大堆错误。我开始想,即使答案出现在线程id 3上,这些事件的监听器仍然是主线程(id 1)。只要它被阻塞,事件就不会被触发。

我说的对吗?我要怎么绕过这个?以不同的方式注册事件?
哦,我差点忘了:我在这里服务一个Interface,这将成为一个复杂应用程序的插件,所以我不能使连接方法异步,并使用异步/等待模式。我必须调用设备的Connect,阻塞主线程直到答案到达,然后释放它,所以应用程序的主要部分可以继续。

有人知道怎么解决这个问题吗?


编辑(第一个):好的,整理一些混淆。这是一个从非异步方法调用的插件。我不能将调用方法更改为异步方法,否则我将不得不重新编程几十万行代码。

调用来自主程序,看起来像这样:firstDevice.Connect();我可以将其更改为合理的东西,但我不能使用像:await firstDevice.Connect();这样的东西,否则我将不得不将主程序调用全部更改为异步。这是毫无疑问的。

我可以更改插件中的connect方法。此时,它除了调用ConnectDevice()之外什么都不做,所以我可以用async、SemaphoreSlims等等来测试一些东西。

只要我在async方法中使用await,调用线程就会继续。还必须有await,但不能在async方法之外使用await

对我来说奇怪的是,Thread.CurrentThread.ManagedThreadId说两个线程都是线程1。但当我一步步走过时,它们显然是异步移动的。


编辑(第二个):我听到了一个线索。也许这里的问题是API的BackgroundWorker。我的一个同事曾经听说,当gui线程启动时,BackgroundWorker阻塞了gui线程。因此,在gui线程被释放之前,API的事件无法到达线程3上的我。因此,解决方案是在不同的线程上调用MobileDevice.Connect();。但似乎API将不得不改变。我们将在内部讨论这个问题。一旦我有了解决方案,我会最后一次为感兴趣的人更新这个。


编辑(第3):好吧,似乎几乎所有这些解决方案都起作用了,我的问题真的是这个该死的BackgroundWorker。API在线程1上与移动设备通信。一旦你阻塞了线程1,在API和设备之间的通信中也有一个阻塞,所以API的答案永远不会出现…不过还是要谢谢你的帮助。;)

你可以把Connect调用封装在TaskCompletionSource:

public static class DeviceExtension
{
public static Task ConnectAsync(this Device device)
{
var tcs = new TaskCompletionSource<object>();
device.DeviceConnected += (s, e) => tcs.SetResult(null);
device.Connect();
return tcs.Task;
}
}

,您可以将其称为

await MobileDevice.ConnectAsync();

或在同步上下文中,如

MobileDevice.ConnectAsync().Wait();

最新更新