Возбуждение событий во время задач, вызывающих перекрестное исключение - PullRequest
0 голосов
/ 22 октября 2018

Я написал асинхронные классы клиент / сервер, которые прекрасно работают в консоли.Я создал проект WinForm для сервера, который подписывается на событие, генерируемое сервером при наличии соединения .Pending(), и записывает некоторые сообщения в текстовое поле, что вызывает исключение Cross-Thread.Исключение не удивляет меня, однако я ищу способ вызвать это событие, не вызывая этого исключения, не обрабатывая его в GUI / Control с помощью .InvokeRequired и .Invoke - если это вообще возможно?

Сервер запускается следующим образом:

Server server = new Server(PORT);
server.RunAsync();

в .RunAsync() Я просто перебираю сетевые устройства и настраиваю их на прослушивание и вызывает событие, запущенное сервером, это также записывается в графический интерфейсоднако без каких-либо проблем.

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 и не вызывает исключение Cross-Thread при записи «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());
                        }

                    }
                }
            }
        }));
    }

Я знаю, что могу использовать текстовое поле .InvokeRequired и .Invoke в графическом интерфейсе, но у меня такое чувство, чтосервер должен генерировать событие так, чтобы графический интерфейс не вызывал исключение Cross-Thread.

Есть ли способ вызвать обработчик событий в этой "бесконечной задаче", не вызывая этого исключения?

1 Ответ

0 голосов
/ 23 октября 2018

Благодаря комментариям и хорошему сну я решил свою проблему, изменив 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.

Улучшение, которое я мог бы добавить, - это принять коллекцию в событии вместо одного соединения.

Если у вас есть какие-либо улучшения или предложения по улучшению практики, пожалуйста, дайте мне знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...