C# 被父线程中的变量求值弄糊涂了
我在一家在外地有多家店铺的公司工作。当我们需要连接到商店POS系统时,我们需要先连接到他们的VPN。我正在开发一个GUI来帮助简化流程。我让它工作得很好,但我偶然发现了一些我不理解的行为 当程序连接到VPN时,它将启动OpenVPN控制台程序,等待程序询问用户名和密码,然后监视程序输出,以检测连接是否成功,跟踪错误消息或何时由于不活动而断开连接 在我的开发机器上,我得到了一致而准确的结果。当我在一台实际机器上运行它时,它无法正确检测VPN连接何时成功。例如 这是监视OpenVPN输出的代码C# 被父线程中的变量求值弄糊涂了,c#,multithreading,C#,Multithreading,我在一家在外地有多家店铺的公司工作。当我们需要连接到商店POS系统时,我们需要先连接到他们的VPN。我正在开发一个GUI来帮助简化流程。我让它工作得很好,但我偶然发现了一些我不理解的行为 当程序连接到VPN时,它将启动OpenVPN控制台程序,等待程序询问用户名和密码,然后监视程序输出,以检测连接是否成功,跟踪错误消息或何时由于不活动而断开连接 在我的开发机器上,我得到了一致而准确的结果。当我在一台实际机器上运行它时,它无法正确检测VPN连接何时成功。例如 这是监视OpenVPN输出的代码 va
var outputProcessingThread = new Thread(() =>
{
while (_trackOpenVpnOutput)
{
var buffer = new byte[256];
IAsyncResult ar = _vpnProcess.StandardOutput.BaseStream.BeginRead(buffer, 0, 256, null, null);
ar.AsyncWaitHandle.WaitOne();
var bytesRead = _vpnProcess.StandardOutput.BaseStream.EndRead(ar);
if (bytesRead > 0)
{
var outputString = Encoding.ASCII.GetString(buffer, 0, bytesRead);
RaiseDebugVpnMessageEvent(new VpnMessageEventArgs(outputString, VpnMessageType.Info));
if (outputString.Contains("Initialization Sequence Completed With Errors"))
{
_trackOpenVpnOutput = false;
_vpnConnected = false;
const string message = "Unable to connect to VPN: Errors in Initialization Sequence";
RaiseVpnMessageEvent(new VpnMessageEventArgs(message, VpnMessageType.Error));
return;
}
if (outputString.Contains("Initialization Sequence Completed"))
{
RaiseVpnMessageEvent(new VpnMessageEventArgs(String.Format("Connected to #{0}'s VPN.", storeNumber), VpnMessageType.Info));
_vpnConnected = true;
}
else if (outputString.Contains("Inactivity timeout"))
{
RaiseVpnMessageEvent(new VpnMessageEventArgs("Inactivity timeout. Disconnected from VPN.", VpnMessageType.Warning));
_vpnConnected = false;
_trackOpenVpnOutput = false;
return;
}
else if (outputString.Contains("TLS Error"))
{
RaiseVpnMessageEvent(new VpnMessageEventArgs("Unable to connect to VPN: TLS Error.", VpnMessageType.Error));
_vpnConnected = false;
_trackOpenVpnOutput = false;
return;
}
}
}
});
然后,在我启动该线程之后,我用这个检查它(等待VPN连接或者它们是错误的,并且输出处理线程将_trackOpenVpnOutput更改为false)
所以我不明白的是,如果我注释掉Console.WriteLine(--message--),那么这些变量在while(!\u vpnConnected&&u trackOpenVpnOutput)循环中的计算似乎不正确
现在,我将把调试控制台.WriteLine留在那里,因为它可以工作,但我想了解为什么我会看到这种行为,并希望了解处理这种情况的正确方法。我猜
\u vpnConnected
\u trackOpenVpnOutput
都是bool
字段
您偶然发现的是多线程更改可见性,此外,编译器能够在假设可见性为零的情况下进行优化。编译器不知道其他线程能够更改这些变量,因此可以自由地优化重复读取
您需要告诉它不要优化这些读取——每次都要重新读取变量。您可以通过将变量标记为volatile
,或使用volatile来执行此操作。请阅读:
while (!Volatile.Read(ref _vpnConnected)
&& Volatile.Read(ref _trackOpenVpnOutput))
考虑在DLL上使用ILDASM,看看IL是否有差异。当取消Console语句时,可能会进行优化。在问题的此处张贴任何IL差异。听起来,至少在\u vpn connected
和\u trackOpenVpnOutput
周围需要某种内存屏障。您可以尝试声明它们,但不查看外部代码。我不确定这是否足够。您是否在开发计算机上测试了发布版本?您是对的,它们都是布尔值。我很抱歉没有说明这一点。我将这些变量的声明更改为volatile,它工作正常。非常感谢你提供的信息。我要去读一下volatile关键字,这样我就能更好地理解它了!推荐阅读有关本书的内容。
while (!Volatile.Read(ref _vpnConnected)
&& Volatile.Read(ref _trackOpenVpnOutput))