Использование Threads, .Invoke () и элементов управления по-прежнему остаются неактивными - C # - PullRequest
0 голосов
/ 07 февраля 2011

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

У меня есть класс, который будет генерировать и возвращать список инструментов, затем я повторяючерез список и добавляйте новую строку в текстовое поле после каждой итерации.

Начало потока:

private void buttonListInstruments_Click(object sender, EventArgs e)
        {
            if (ins == null)
            {
                ins = new Thread(GetListOfInstruments);
                ins.Start();
            }
            else if (ins != null)
            {
                textBoxLog.AppendText("Instruments still updating..");
            }

        }

Делегат для обновления текстового поля:

public delegate void UpdateLogWithInstrumentsCallback(List<Instrument> instruments);

private void UpdateInstruments(List<Instrument> instruments)
        {
            textBoxLog.AppendText("Listing available Instruments...\n");

            foreach (var value in instruments)
            {
                textBoxLog.AppendText(value.ToString() + "\n");
            }
            textBoxLog.AppendText("End of list. \n");

            ins = null;
        }

Вызов элемента управления:

private void GetListOfInstruments()
        {
            textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
                new object[] { midiInstance.GetInstruments() });
        }

Примечание: GetInstruments () возвращает Список типа Инструмент.

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

Правильно ли я использую потоки?

Спасибо.

Ответы [ 6 ]

3 голосов
/ 07 февраля 2011

Вы ничего не сделали, метод UpdateInstruments () по-прежнему работает в потоке пользовательского интерфейса, как и раньше.Не уверен, почему вы видите такую ​​длительную задержку, что должно быть большое количество инструментов.Вы можете сделать его менее медленным, сначала добавив их все в StringBuilder, а затем добавьте его значение ToString () в TextBox.Это отсекает довольно дорогой вызов Windows.

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

Я бы рекомендовал использовать SynchronizationContext в целом:

Из потока пользовательского интерфейса, например, инициализация:

// make sure a SC is created automatically
Forms.WindowsFormsSynchronizationContext.AutoInstall = true;
// a control needs to exist prior to getting the SC for WinForms
// (any control will do)
var syncControl = new Forms.Control();
syncControl.CreateControl();
SyncrhonizationContext winformsContext = System.Threading.SynchronizationContext.Current;

Позже, из любого потока, желающего опубликоватьна вышеуказанный SC:

// later on -- no need to worry about Invoke/BeginInvoke! Whoo!
// Post will run async and will guarantee a post to the UI message queue
// that is, this returns immediately
// it is OKAY to call this from the UI thread or a non-UI thread
winformsContext.Post(((state) => ..., someState);

Как уже отмечали другие, либо ускорите действие по обновлению пользовательского интерфейса ( это лучший метод !!! ), либо разделите его на несколько действий отправлено в очередь пользовательского интерфейса (если вы отправите сообщение в очередь, другие сообщения в очереди не будут заблокированы).Вот пример «разбивки» операций на небольшие промежутки времени, пока все это не будет выполнено - предполагает, что UpdateStuff вызывается после сбора данных и не обязательно подходит, когда сбор данных занимает заметное время .Это не принимает во внимание «остановку» и является своего рода грязным, поскольку вместо прохождения состояния используется закрытие.В любом случае, наслаждайтесь.

void UpdateStuff (List<string> _stuff) {
    var stuff = new Queue<string>(_stuff); // make copy
    SendOrPostCallback fn = null; // silly so we can access in closure
    fn = (_state) => {
        // this is in UI thread
        Stopwatch s = new Stopwatch();
        s.Start();
        while (s.ElapsedMilliseconds < 20 && stuff.Count > 0) {
          var item = stuff.Dequeue();
          // do stuff with item
        }
        if (stuff.Count > 0) {
          // have more stuff. we may have run out of our "time-slice"
          winformsContext.Post(fn, null);
        }
    };
    winformsContext.Post(fn, null);
}

Удачного кодирования.

0 голосов
/ 07 февраля 2011

Возможно, вы обнаружите, что эффективнее сначала создать строку и добавить ее в текстовое поле одним фрагментом, а не построчно. Операция конкатенации строк может затем выполняться и во вспомогательном потоке.

0 голосов
/ 07 февраля 2011

Нет, вы запускаете поток, а затем используете invoke, что в основном означает, что вы возвращаетесь к потоку пользовательского интерфейса для выполнения работы ... так что ваш поток ничего не делает!

0 голосов
/ 07 февраля 2011

Вы вводите все инструменты в текстовое поле одновременно, а не по одному с точки зрения потоков. Вызов Invoke должен быть помещен в цикл for и не окружать его.

0 голосов
/ 07 февраля 2011

Изменить эту строку:

textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
                new object[] { midiInstance.GetInstruments() });

с этим:

textBoxLog.BeginInvoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
                new object[] { midiInstance.GetInstruments() });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...