У меня есть форма окна в основном потоке и еще один поток, который выполняет некоторые вычисления. Я хотел бы обновить строку состояния в моей форме из работы, выполняемой в другом потоке. Как лучше всего это сделать?
До сих пор все, что я пробовал, просто замедляло работу. Я использую Visual Studio 2005.
Ответ 1 · рейтинг 2 · Rob
Убедитесь, что вы обновляете пользовательский интерфейс только из основного потока, иначе у вас возникнут проблемы. Вы можете переключить контекст своего потока, вызвав Invoke. Об этом есть хороший пост здесь.
Ответ 2 · рейтинг 3 · Brian Gideon
Вы можете использовать методы маршалинга, такие как Control.Invoke, для выполнения делегата в потоке пользовательского интерфейса, где можно безопасно манипулировать элементами пользовательского интерфейса, но этот подход не очень хорош. На самом деле, это ужасный подход, если все, что вы хотите сделать, это обновить простую информацию о прогрессе.
На сегодняшний день лучший способ сделать это:
- Пусть ваш рабочий поток публикует информацию о ходе выполнения в общей переменной.
- Попросите ваш поток пользовательского интерфейса опросить его через
System.Windows.Forms.Timers с интервалом, который удобен для вас.
Вот как это может выглядеть.
public class Example : Form
{
private volatile int percentComplete = 0;
private void StartThreadButton_Click(object sender, EventArgs args)
{
StatusBarUpdateTimer.Enabled = true;
new Thread(
() =>
{
for (int i = 1; i <= 100; i++)
{
DoSomeWork();
percentComplete = i;
}
}).Start();
}
private void StatusBarUpdateTimer_Tick(object sender, EventArgs args)
{
yourStatusBarPanel.Text = percentComplete.ToString() + "%";
StatusBarUpdateTimer.Enabled = percentComplete < 100;
}
}
Это хорошо работает, потому что:
- Поле PercentComplete объявлено "изменчивым", что гарантирует возможность надежного чтения его значения из нескольких потоков.
- Поток пользовательского интерфейса должен диктовать, когда и как часто будет обновляться пользовательский интерфейс... так, как это должно быть!
- Рабочий поток не должен ждать ответа от потока пользовательского интерфейса, прежде чем он сможет продолжить работу, как в случае с
Invoke.
- Это разрушает тесную связь между пользовательским интерфейсом и рабочими потоками, которую навязывает
Invoke.
- Это более эффективно... значительно.
- Вы получаете большую пропускную способность как в пользовательском интерфейсе, так и в рабочих потоках.
- Нет возможности переполнить очередь сообщений пользовательского интерфейса, как это может быть в случае с
BeginInvoke.
- Вам не нужно засорять свой код вызовами
Invoke каждый раз, когда вам нужно обновить пользовательский интерфейс из рабочего потока.
Ответ 3 · рейтинг 1 · Colen
Вы можете отправлять сообщения в основной поток и заставлять его обновлять индикатор выполнения, хотя затем вам нужно проверять сообщения. Вы также можете сделать то же самое, что и функция опроса.