繁体   English   中英

在任务期间引发事件导致跨线程异常

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM