Недопустимая операция между потоками: доступ к элементу управления из потока, отличного от потока, в котором он был создан - PullRequest
3 голосов
/ 23 января 2012

Я знаю, что этот вопрос задавался несколько раз, но я не могу понять, почему он так поступает в моей ситуации.

Прежде всего, я немного объясню свою программу.Он подключается к аппаратному устройству через чип FTDI, поэтому он генерирует нам COM через USB.Мои программы запускаются, это интерфейс MDI.При нажатии кнопки «Подключить» открывается окно подключения, аналогичное окну «Добавить устройство» в Windows.Он сканирует все COM на компьютере и пытается подключиться к нему, чтобы сообщить, что это за устройство.После этого пользователь нажимает на устройство, подключается к нему, и открывается дочерняя форма для управления этим устройством.

Итак, моя проблема в том, что у меня много многопоточности.При первом подключении к устройству все работает нормально.Во второй раз он возвращает ошибку операции с несколькими потоками.

Это краткий пример моего кода:

    private void ConnectToolStripButton_Click(object sender, EventArgs e)
    {

        Dialogs.Connect Connect = new Dialogs.Connect();
        if (Connect.ShowDialog() == DialogResult.OK)
        {
            this.Connect(Connect.Connection);
        }

    }

    private void Connect(CommunicationInterfaces.Base Connection)
    {

        // Set the connection to the one the connect dialog gave us.
        Child NewConnection = new Child(Connection);

        // Set the parent of the new child and show it.
        NewConnection.MdiParent = this;
        NewConnection.Show(); // CRASH HERE!

    }

Так что он падает на .show () со следующей ошибкой, но только во второй раз, когда я подключаюсь к нему: Cross-thread operation not valid: Control 'Child' accessed from a thread other than the thread it was created on.

Объект-это дочерний объект (имя моей дочерней формы) создается в потоке пользовательского интерфейса, если я не ошибаюсь.Почему это дает мне ошибку межпотоковой операции тогда?Это проблема в моей дочерней форме?

Обновление: Таймер поддержания активности

Так что я смог уточнить проблему чуть больше.Проблема заключается в том, что у меня есть ветка Keep Alive, которая есть в форме моего ребенка.Чтобы объяснить ситуацию: у меня есть соединение, которое необходимо поддерживать в рабочем состоянии, поэтому каждые 500 мс у меня запускается поток для отправки специального заголовка на мое устройство.Это мой код потока активности:

    private void Child_Shown(object sender, EventArgs e)
    {

        this.Connection.DataReceived += DisplayData;

        ...

    }

    private void DisplayData(object Sender, byte[] Data)
    {

        ...

                CreateFaultBox((FaultBoxes.Base.BoxTypes)Data[1]);

        ...

    }

    private void CreateFaultBox(FaultBoxes.Base.BoxTypes BoxType)
    {

        KeepAliveTimer = new System.Threading.Thread(new System.Threading.ThreadStart(this.KeepAlive));
        KeepAliveSwitch = true;
        KeepAliveTimer.Start();

        ...

    }

    private void KeepAlive()
    {

        while (Connection != null && KeepAliveSwitch)
        {

            Console.WriteLine("KEEP ALIVE");

            // Keep the connection alive.
            Connection.KeepAlive();

            // Wait 500ms for the next keep alive.
            System.Threading.Thread.Sleep(500);

        }

    }

Если я удаляю первые 3 строки, поэтому, если я не запускаю поток, он работает без каких-либо отклонений.Конечно, KeepAliveSwitch устанавливается в значение false, когда я закрываю форму, поэтому получение потока поддержки активности прекращается после следующего периода ожидания 500 мс.

Решение

Я изменил поток поддержки активности на фонработник.Работает отлично.Но я не вижу разницы между потоком и фоновым рабочим, не должны ли оба работать одинаково в этом сценарии?

1 Ответ

4 голосов
/ 23 января 2012

Происходит ли какое-нибудь продвижение в дочерней форме?Если так, то это моя теория:

То, что вы, вероятно, видите, это состояние гонки, при котором при первом запуске клиента клиентская форма занята подключением к какому-либо устройству в фоновом потоке, в то время как в это времяваш поток родительского интерфейса MDI Show () является дочерней формой (и поэтому владеет дескриптором окна, и все хорошо).Во второй раз, когда вы показываете клиент, вы получаете кэшированное соединение, и поэтому фоновый поток в дочернем элементе очень быстро подключается, а затем вызывает некоторую операцию пользовательского интерфейса, вероятно, проверяя, как хороший разработчик, используя InvokeRequired ().Поскольку в вашей форме клиента еще нет дескриптора, фоновый поток получает ложное значение для InvokeRequired, а затем вызывает и создает сам дескриптор.

Все это задокументировано в великом посте Ивана Кривякова 1006 * по этому вопросу.

Так что, если все вышеперечисленное звучит правильно, просто не начинайте фоновую работу в дочерней форме, пока не будет создан дескриптор.Возможно, вы захотите повесить это на событие Form Shown , а не на конструктор.

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