Недопустимая операция между потоками: элемент управления 'listBox1' доступен из потока, отличного от потока, в котором он был создан - PullRequest
0 голосов
/ 03 октября 2018

Я хочу добавить элементы в listBox1 в Modbus_request_event. Я прошел через решение, предоставленное для этой проблемы, и изменил код с помощью MethodInvoker Delegate, и все же он не будет добавлять элементы в listBox1.Вот мой код

     public void Modbus_Request_Event(object sender, ModbusSlaveRequestEventArgs e)
        {
            //disassemble packet from master
            byte fc = e.Message.FunctionCode;
            byte[] data = e.Message.MessageFrame;
            byte[] byteStartAddress = new byte[] { data[3], data[2] };
            byte[] byteNum = new byte[] { data[5], data[4] };
            short StartAddress = BitConverter.ToInt16(byteStartAddress, 0);
            short NumOfPoint = BitConverter.ToInt16(byteNum, 0);


            string fc1 = Convert.ToString(fc);
           string StartAddress1 = Convert.ToString(StartAddress);
           string NumOfPoints1 = Convert.ToString(NumOfPoint);

           /*Adds the items to listBox1*/
            Invoke(new MethodInvoker(delegate () { listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1); }));
//it runs infinitely not able to add to listbox//


        }

Может кто-нибудь, пожалуйста, помогите мне решить эту проблему?

Ответы [ 3 ]

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

Прежде всего, дубликат является дубликатом.Вы не можете изменить пользовательский интерфейс из другого потока.До .NET 4.5 люди использовали Invoke или BeginInvoke на элементе управления , чтобы маршалировать делегат в потоке пользовательского интерфейса и запускать его там.Код вопроса, тем не менее, вызывает Invoke() сам по себе, фактически выполняя делегат в потоке, в котором он в данный момент находится.

Короче говоря:

Invoke(new MethodInvoker(delegate () { listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1); }));

По сути то же самое, что и эточто касается потоков:

listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1);

В .NET 4.5 и более поздних версиях вы можете использовать Progress для отчета о прогрессе из любого потока или задачи, как показано в Включение выполнения и отмены в API-интерфейсах Async ,Учитывая, что самая ранняя поддерживаемая версия .NET - это 4.5.2, вы можете предположить, что класс доступен везде.

Используя интерфейс Progress<T> и IProgress<T>, вы можете отделить .событие из пользовательского интерфейса, что означает, что вы можете обрабатывать данные любым способом, даже в разных формах.Вы можете переместить класс Modbus в другой класс или библиотеку, чтобы отделить его от пользовательского интерфейса.

В простейшем случае вы можете создать экземпляр класса Progress<T> в конструкторе вашей формы и вызвать его через IProgress<T> интерфейс из обработчика событий, например:

public class ModbusData
{
    public byte Fc {get; set;}
    public short StartAddress {get; set;}
    public short NumOfPoints {get; set;}
}

public class MyForm : ...
{

    IProgress<ModbusData> _modbusProgress;

    public MyForm()
    {
        __modbusProgress=new Progress<ModbusData>(ReportProgress);
    }

    public void ReportProgress(ModbusData data)
    {
        listBox1.Items.Add(data.fc1.ToString()); 
        listBox1.Items.Add(dta.StartAddress1.ToString()); 
        listBox1.Items.Add(data.NumOfPoints1.ToString()); 
    }

и отчет о ходе выполнения события, независимо от того, на каком потоке он возник:

    public void Modbus_Request_Event(object sender, ModbusSlaveRequestEventArgs e)
    {
        //disassemble packet from master
        byte fc = e.Message.FunctionCode;
        byte[] data = e.Message.MessageFrame;
        byte[] byteStartAddress = new byte[] { data[3], data[2] };
        byte[] byteNum = new byte[] { data[5], data[4] };
        short StartAddress = BitConverter.ToInt16(byteStartAddress, 0);
        short NumOfPoint = BitConverter.ToInt16(byteNum, 0);

        var modData = new ModbusData {
                 Fc = fc,
                 StartAddress = StartAddress,
                 NumOfPoints = NumOfPoint
        };
       _progress.Report(modData);
    }        

Если вы решили переместить классы Modbusдругому классу все, что вам нужно сделать, это передать им экземпляр IProgress<ModbusData>, прежде чем вы начнете его использовать.

Например:

class MyModbusController
{
    IProgress<ModbusData> _modbusProgress;

    public MyModbusController(IProgress<ModbusData> progress)
    {
        _modbusProgress=progress;
    }

    public void Modbus_Request_Event(...)
}
0 голосов
/ 03 октября 2018

Попробуйте использовать следующий код:

Invoke((MethodInvoker)delegate 
{
listBox1.Items.Add(fc1);
listBox1.Items.Add(StartAddress1);
listBox1.Items.Add(NumOfPoints1);
});
0 голосов
/ 03 октября 2018

Это не решает вашу проблему .., потому что ваш код не выглядит неправильно, но это может помочь в принятии лучших решений о потоках и графическом интерфейсе.

Вы используете Invoke для синхронизации Modbusнить и gui-нить.Это означает, что при получении события Modbus вы добавляете элементы в список.Когда он вызывает поток GUI, ваш поток Modbus блокируется.Таким образом, скорость вашего соединения по Modbus зависит от скорости компонентов графического интерфейса.Старайтесь избегать таких неприятных блоков.Если вы получаете много событий Modbus в секунду, ваш список будет заполнен перерисовками, а ваше приложение будет ограничено.

Как это исправить?:

Вы должны добавить элементы в список (concurrentcollection) и использовать таймер для обновления вашего графического интерфейса.Таким образом, вы можете определить, когда полученные события добавляются (в пакете) в список.Достаточно таймера с интервалом 100 мс.

Пример псевдо-кода:

public class Data
{
    public string Fc1 {get; set;}
    public string StartAddress1 {get; set;}
    public string NumOfPoints1 {get; set;}
}

ConcurrentQueue<Data> _modbusEvents = new ConcurrentQueue<Data>();


public void Modbus_Request_Event(object sender, ModbusSlaveRequestEventArgs e)
{
    //disassemble packet from master
    byte fc = e.Message.FunctionCode;
    byte[] data = e.Message.MessageFrame;
    byte[] byteStartAddress = new byte[] { data[3], data[2] };
    byte[] byteNum = new byte[] { data[5], data[4] };
    short StartAddress = BitConverter.ToInt16(byteStartAddress, 0);
    short NumOfPoint = BitConverter.ToInt16(byteNum, 0);


    string fc1 = Convert.ToString(fc);
    string StartAddress1 = Convert.ToString(StartAddress);
    string NumOfPoints1 = Convert.ToString(NumOfPoint);

    // add to the concurrentqueue
    _modbusEvents.Add(new Data { Fc1 = fc1, StartAddress1 = StartAddress1, NumOfPoints1 = NumOfPoints1 });

   /*Adds the items to listBox1*/
    //Invoke(new MethodInvoker(delegate () { listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1); }));
//it runs infinitely not able to add to listbox//

}

private void Timer_Tick(object sender, EventArgs e)
{
    if(_modbusEvents.Count > 0)
    {
        listBox1.BeginUpdate();

        while(_modbusEvents.TryDequeue(out var result))
        {
             listBox1.Items.Add(result.Fc1);
             listBox1.Items.Add(result.StartAddress1);
             listBox1.Items.Add(result.NumOfPoints1);
        }

        listBox1.EndUpdate();
    }
}
...