我不知道是否有人仍在寻找解决此问题的方法,但我已经多次遇到这种情况,因为我正在 Unity 中编写一个工具来支持某些游戏,并且由于某些游戏的互操作性有限具有单声道的系统(例如,用于从 Word 读取文本的 PIA),我经常必须编写特定于操作系统(有时是 Windows,有时是 MacOS)的可执行文件并从 Process.Start() 启动它们。
问题是,当您启动这样的可执行文件时,它会在另一个线程中启动,从而阻塞您的主应用程序,从而导致挂起。如果您想在这段时间内向您的用户提供有用的反馈,而不是您各自的操作系统产生的旋转图标,那么您有点搞砸了。使用流是行不通的,因为线程在执行完成之前仍然被阻塞。
我想到的解决方案(对某些人来说可能看起来很极端,但我发现对我来说效果很好)是使用套接字和多线程在两个应用程序之间建立可靠的同步通信。当然,这仅在您同时编写两个应用程序时才有效。如果没有,我认为你不走运。 ...我想看看它是否适用于使用传统流方法的多线程,所以如果有人想尝试并在此处发布结果,那就太好了。
无论如何,这是目前对我有用的解决方案:
在主应用程序或调用应用程序中,我执行以下操作:
/// <summary>
/// Handles the OK button click.
/// </summary>
private void HandleOKButtonClick() {
string executableFolder = "";
#if UNITY_EDITOR
executableFolder = Path.Combine(Application.dataPath, "../../../../build/Include/Executables");
#else
executableFolder = Path.Combine(Application.dataPath, "Include/Executables");
#endif
EstablishSocketServer();
var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = Path.Combine(executableFolder, "WordConverter.exe"),
Arguments = locationField.value + " " + _ipAddress.ToString() + " " + SOCKET_PORT.ToString(),
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
这里是我建立套接字服务器的地方:
/// <summary>
/// Establishes a socket server for communication with each chapter build script so we can get progress updates.
/// </summary>
private void EstablishSocketServer() {
//_dialog.SetMessage("Establishing socket connection for updates. \n");
TearDownSocketServer();
Thread currentThread;
_ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
_listener = new TcpListener(_ipAddress, SOCKET_PORT);
_listener.Start();
UnityEngine.Debug.Log("Server mounted, listening to port " + SOCKET_PORT);
_builderCommThreads = new List<Thread>();
for (int i = 0; i < 1; i++) {
currentThread = new Thread(new ThreadStart(HandleIncomingSocketMessage));
_builderCommThreads.Add(currentThread);
currentThread.Start();
}
}
/// <summary>
/// Tears down socket server.
/// </summary>
private void TearDownSocketServer() {
_builderCommThreads = null;
_ipAddress = null;
_listener = null;
}
这是我的线程套接字处理程序...请注意,在某些情况下您必须创建多个线程;这就是为什么我在那里有那个 _builderCommThreads 列表的原因(我从其他地方的代码中移植了它,我正在做类似的事情但连续调用多个实例):
/// <summary>
/// Handles the incoming socket message.
/// </summary>
private void HandleIncomingSocketMessage() {
if (_listener == null) return;
while (true) {
Socket soc = _listener.AcceptSocket();
//soc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000);
NetworkStream s = null;
StreamReader sr = null;
StreamWriter sw = null;
bool reading = true;
if (soc == null) break;
UnityEngine.Debug.Log("Connected: " + soc.RemoteEndPoint);
try {
s = new NetworkStream(soc);
sr = new StreamReader(s, Encoding.Unicode);
sw = new StreamWriter(s, Encoding.Unicode);
sw.AutoFlush = true; // enable automatic flushing
while (reading == true) {
string line = sr.ReadLine();
if (line != null) {
//UnityEngine.Debug.Log("SOCKET MESSAGE: " + line);
UnityEngine.Debug.Log(line);
lock (_threadLock) {
// Do stuff with your messages here
}
}
}
//
} catch (Exception e) {
if (s != null) s.Close();
if (soc != null) soc.Close();
UnityEngine.Debug.Log(e.Message);
//return;
} finally {
//
if (s != null) s.Close();
if (soc != null) soc.Close();
UnityEngine.Debug.Log("Disconnected: " + soc.RemoteEndPoint);
}
}
return;
}
当然,您需要在顶部声明一些内容:
private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _builderCommThreads = null;
private System.Object _threadLock = new System.Object();
...然后在调用的可执行文件中,设置另一端(在这种情况下我使用静态,你可以使用任何你想要的):
private static TcpClient _client = null;
private static Stream _s = null;
private static StreamReader _sr = null;
private static StreamWriter _sw = null;
private static string _ipAddress = "";
private static int _port = 0;
private static System.Object _threadLock = new System.Object();
/// <summary>
/// Main method.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args) {
try {
if (args.Length == 3) {
_ipAddress = args[1];
_port = Convert.ToInt32(args[2]);
EstablishSocketClient();
}
// Do stuff here
if (args.Length == 3) Cleanup();
} catch (Exception exception) {
// Handle stuff here
if (args.Length == 3) Cleanup();
}
}
/// <summary>
/// Establishes the socket client.
/// </summary>
private static void EstablishSocketClient() {
_client = new TcpClient(_ipAddress, _port);
try {
_s = _client.GetStream();
_sr = new StreamReader(_s, Encoding.Unicode);
_sw = new StreamWriter(_s, Encoding.Unicode);
_sw.AutoFlush = true;
} catch (Exception e) {
Cleanup();
}
}
/// <summary>
/// Clean up this instance.
/// </summary>
private static void Cleanup() {
_s.Close();
_client.Close();
_client = null;
_s = null;
_sr = null;
_sw = null;
}
/// <summary>
/// Logs a message for output.
/// </summary>
/// <param name="message"></param>
private static void Log(string message) {
if (_sw != null) {
_sw.WriteLine(message);
} else {
Console.Out.WriteLine(message);
}
}
...我正在使用它在 Windows 上启动一个命令行工具,该工具使用 PIA 内容从 Word 文档中提取文本。我在 Unity 中尝试了 PIA .dll,但遇到了单声道的互操作问题。我还在 MacOS 上使用它来调用 shell 脚本,这些脚本以批处理模式启动其他 Unity 实例,并在那些通过此套接字连接与工具对话的实例中运行编辑器脚本。这很棒,因为我现在可以向用户发送反馈、调试、监控和响应流程中的特定步骤等等。
HTH