Нужно объяснение по обработке событий и делегатов - PullRequest
2 голосов
/ 23 февраля 2011

У меня есть код, который я написал, который делает то, что я хочу.Однако я не совсем уверен, как именно это работает.Часть, с которой у меня больше всего проблем, это последняя часть.У меня был textBox1.Text = "test", который не работал.Я получил сообщение об ошибке во время выполнения вызова из другого потока.Когда я поставил textBox1.Invoke (и т. Д.), Он работал как положено.Зачем?

Как видите, я знаю достаточно, чтобы быть опасным, и я действительно хочу понять, что здесь происходит, вместо того, чтобы слепо копировать и вставлять сайты по всему Интернету.

У меня есть следующеев классе с именем SerialCommunicator:

public SerialCommunicator(SerialPort sp)
{
    this.sp = sp;
    sp.ReceivedBytesThreshold = packetSize;
    sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
    sp.Open();
}    
public void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(50);
    SerialPort s = (SerialPort)sender;
    byte[] buffer = new byte[128];
    s.Read(buffer, 0, s.BytesToRead);
}

Затем в моем Form1.cs у меня есть кнопка, которая при нажатии делает следующее:

private void btnPortOK_Click(object sender, EventArgs e)
{
    string comPort = cboComPorts.SelectedItem.ToString();
    SerialPort sp = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One);
    sp.DataReceived += new SerialDataReceivedEventHandler(DataHasBeenReceived);
    comm = new SerialCommunicator(sp);
}
public void DataHasBeenReceived(object sender, EventArgs args)
{
    textBox1.Invoke(new EventHandler(delegate { textBox1.Text += "test"; }));
}

Ответы [ 5 ]

4 голосов
/ 23 февраля 2011

Это нить-сродство.Элементы управления пользовательского интерфейса не любят, когда к ним прикасается что-либо, кроме потока, который их создал, но поток DataReceived происходит из другого потока.Добавление вызова к Control.Invoke возвращает элемент работы обратно в поток пользовательского интерфейса, поэтому обновление Text может быть успешным.

2 голосов
/ 23 февраля 2011

Я не эксперт в этом вопросе (вероятно, будут лучшие ответы, чем этот). Но, насколько я понимаю, поток GUI «владеет» вашей формой. Поэтому, когда вы пытаетесь обновить его из другого потока, вы пересекаете потоки.

Invoke - это способ попросить поток GUI запустить метод. Метод, который он запускает - ваш textBox1.Text += "test";

Идея состоит в том, чтобы вызвать делегата, который попросит поток GUI внести изменение, а не просто изменить значение самостоятельно. Это позволяет сделать изменение безопасным для потоков.

Вот хорошая статья Джона Скита по этому вопросу: http://www.yoda.arachsys.com/csharp/threads/winforms.shtml

1 голос
/ 23 февраля 2011

Проблема в том, что компоненты GUI принимают изменения только из потока GUI.Поэтому, когда другие потоки хотят изменить GUI, они должны поставить в очередь свой код модификации, используя такие меры, как control.Invoke(...), которые будут ставить в очередь делегата для обработки как можно скорее в очереди событий GUI, и, следовательно, правильный поток.

То, что вы запускаете, состоит в том, что одна из встроенных проверок запускается, чем контролирует, что вызывающий поток действительно является правильным потоком.Это мера безопасности, которая облегчает отладку (если их нет, вам придется вместо этого отлаживать тонкие проблемы с многопоточностью ...)

1 голос
/ 23 февраля 2011

textBox1.Text = "test" не работает, потому что вы вызываете его из другого потока (то есть события DataHasBeenReceived), а затем потока, которому принадлежит текстовое поле. Обычно это поток, в котором выполняется ваше приложение, и который создает интерфейс GUI (и, следовательно, текстовое поле). Invoke работает, потому что этот метод переключается на поток GUI, устанавливает ваш текст, а затем переключается обратно на поток вашего DataHasBeenReceived события.

В Net 1.0 и 1.1 вы могли использовать элементы управления с графическим интерфейсом из другого потока, а не того, которому они принадлежали, но это привело к множеству проблем, когда потоки начали получать доступ к элементам управления одновременно. Итак, начиная с net 2.0 Microsoft изменила это

Если вы хотите знать, нужно ли использовать invoke или нет (то есть, если метод может быть вызван как из потока GUI, так и из другого потока), вы можете использовать свойство InvokeRequired в сочетании с if else , Вызов invoke немного дороже, чем прямое манипулирование элементом управления.

1 голос
/ 23 февраля 2011

События вызываются из потока, в котором они происходят.(Если не указано иное).

Подумайте так:
Когда вы активируете событие, оно на самом деле называется функцией EventName ().Таким образом, вызов события означает на самом деле обращение ко всем методам, которые были зарегистрированы для этого события, и выполнение их.
Но это выполняется в одном и том же потоке последовательным способом.

Таким образом, если событие произошло в потоке, который не является вашим потоком пользовательского интерфейса, вы получите ошибку theat.

...