我编写了一个Client / Server异步类,该类在控制台中可以正常工作。我为服务器创建了一个WinForm项目,该项目订阅了存在.Pending()
连接时服务器抛出的事件,并将一些消息写入文本框-这会导致跨线程异常。异常不会令我感到惊讶,但是我正在寻找一种方法来调用该事件而不会导致此异常,而不用.InvokeRequired
和.Invoke
在GUI / Control上对其进行处理-如果可能的话?
服务器启动如下:
Server server = new Server(PORT);
server.RunAsync();
在.RunAsync()
中,我只是遍历网络设备,并将它们设置为侦听并调用服务器已启动的事件,这也将写入GUI,但是没有任何问题。
public async Task RunAsync()
{
GetNetworkDevicesReady(Port);
await Task.Factory.StartNew(() =>
{
Parallel.ForEach(networkListeners, (listener) =>
{
Write.Info($"LISTENING ON {listener.LocalEndpoint}");
listener.Start();
});
});
IsRunning = true;
OnServerStarted?.Invoke(this, networkListeners.Where(l=>l.Active).ToList());
}
下面的代码在Form.Load
事件中注册,并且在文本框中编写“ SERVER STARTED”时不会引起跨线程异常。
server.OnServerStarted += (s, a) =>
{
consoleWindow1.Event("SERVER STARTED", $"{Environment.NewLine}\t{string.Join($"{Environment.NewLine}\t", a.Select(x=>x.LocalEndpoint))}");
consoleWindow1.Event("WAITING FOR PENDING CONNECTIONS");
server.WaitForConnectionsAsync();
};
这是无限期运行的代码,直到触发取消令牌:
public async Task WaitForConnectionsAsync()
{
waitingForConnectionsToken = new CancellationTokenSource();
await (waitinfConnectionTaks=Task.Factory.StartNew(async () =>
{
while (!waitingForConnectionsToken.IsCancellationRequested)
{
foreach (var listener in networkListeners)
{
if (waitingForConnectionsToken.IsCancellationRequested) break;
if (!listener.Active)
{
continue;
}
if (listener.Pending())
{
try
{
TcpClient connection = await listener.AcceptTcpClientAsync();
//TODO: need to send it synchronised, since this causes a Cross-Thread when using WinForms
OnPendingConnection?.Invoke(this, connection);
}
catch (ObjectDisposedException x)
{
Write.Error(x.ToString());
}
}
}
}
}));
}
我知道我可以在GUI上使用文本框.InvokeRequired
和.Invoke
,但是我感觉服务器应该以GUI不会引起跨线程异常的方式抛出事件。
是否有一种方法可以在该“无限任务”中调用事件处理程序而不会导致此异常?
答案 0 :(得分:0)
多亏了评论和充足的睡眠,我通过将WaitForConnectionsAsync
更改为以下代码解决了我的问题:
List<TcpClient> connections = new List<TcpClient>();
public async Task WaitForConnectionsAsync()
{
await (waitinfConnectionTaks = Task.Factory.StartNew(async () =>
{
//REMOVED LOOP
// while (!waitingForConnectionsToken.IsCancellationRequested)
{
foreach (var listener in networkListeners)
{
if (waitingForConnectionsToken.IsCancellationRequested) break;
if (!listener.Active)
{
continue;
}
if (listener.Pending())
{
try
{
TcpClient connection = await listener.AcceptTcpClientAsync();
//RETAIN CONNECTIONS IN A LIST
connections.Add(connection);
}
catch (ObjectDisposedException x)
{
Write.Error(x.ToString());
}
}
}
}
}));
//ITERATE OVER CONNECTIONS
foreach (var connection in connections)
{
//INVOKE EVENT
OnPendingConnection?.Invoke(this, connection);
}
//CLEAR THE LIST
connections.Clear();
//RESTART THE TASK
if(!waitingForConnectionsToken.IsCancellationRequested)
WaitForConnectionsAsync();
}
因此,基本上,我将所有未决的连接捕获到列表中,一旦工作完成,我将遍历该列表,使用每个连接触发该事件,清除列表,然后再次启动任务。此代码更改不再引发Cross-Thread异常。
我现在要添加的一项改进是在事件中接受一个集合,而不是单个连接。
如果您有任何改进或更好的实践建议,请告诉我。