[英]Task ContinueWith causing cross-thread exception despite UI context
[英]Raising events during Tasks causing Cross-Thread Exception
我编写了一个Client / Server异步类,该类在控制台中可以正常工作。 我为服务器创建了一个WinForm项目,当存在.Pending()
连接时,该项目订阅了服务器引发的事件,并将一些消息写入文本框-导致跨线程异常。 异常不会令我惊讶,但是我正在寻找一种方法来调用该事件而不会导致此异常,而无需使用.InvokeRequired
和.Invoke
在GUI / Control上.InvokeRequired
进行.Invoke
-如果可能的话?
这样启动服务器:
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不会导致跨线程异常的方式抛出事件。
有没有一种方法可以在该“无限任务”中调用事件处理程序而不会导致此异常?
多亏了注释和充足的睡眠,我通过将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异常。
我现在可以添加的一项改进是在事件中接受一个集合,而不是单个连接。
如果您有任何改进或更好的做法建议,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.