товарищи) Я нашел интересное поведение метода Invalidate в многопоточных приложениях. Надеюсь, вы могли бы помочь мне с проблемой ...Задача очереди GDI + Paint
У меня возникают проблемы при попытке аннулировать различные элементы управления за один раз: в то время как они идентичны, одна успешно перекрашивается, а другая - нет.
Ниже приведен пример: У меня есть форма (MysticForm) с двумя панелями (SlowRenderPanel) на ней. Каждая панель имеет таймер и с периодом 50 мс вызывается метод Invalidate(). В методе OnPaint я рисую число текущего вызова OnPaint в центре панели. Но заметьте, что в методе OnPaint System.Threading.Thread.Sleep (50) вызывается для моделирования длительной процедуры рисования.
Таким образом, проблема заключается в том, что добавленная панель сначала реплицирует себя гораздо чаще, чем другая.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1 {
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MysticForm());
}
}
public class MysticForm : Form {
public SlowRenderPanel panel1;
public SlowRenderPanel panel2;
public MysticForm() {
// add 2 panels to the form
Controls.Add(new SlowRenderPanel() { Dock = DockStyle.Left, BackColor = Color.Red, Width = ClientRectangle.Width/2 });
Controls.Add(new SlowRenderPanel() { Dock = DockStyle.Right, BackColor = Color.Blue, Width = ClientRectangle.Width/2 });
}
}
public class SlowRenderPanel : Panel {
// synchronized timer
private System.Windows.Forms.Timer timerSafe = null;
// simple timer
private System.Threading.Timer timerUnsafe = null;
// OnPaint call counter
private int counter = 0;
// allows to use one of the above timers
bool useUnsafeTimer = true;
protected override void Dispose(bool disposing) {
// active timer disposal
(useUnsafeTimer ? timerUnsafe as IDisposable : timerSafe as IDisposable).Dispose();
base.Dispose(disposing);
}
public SlowRenderPanel() {
// anti-blink
DoubleBuffered = true;
// large font
Font = new Font(Font.FontFamily, 36);
if (useUnsafeTimer) {
// simple timer. starts in a second. calls Invalidate() with period = 50ms
timerUnsafe = new System.Threading.Timer(state => { Invalidate(); }, null, 1000, 50);
} else {
// safe timer. calls Invalidate() with period = 50ms
timerSafe = new System.Windows.Forms.Timer() { Interval = 50, Enabled = true };
timerSafe.Tick += (sender, e) => { Invalidate(); };
}
}
protected override void OnPaint(PaintEventArgs e) {
string text = counter++.ToString();
// simulate large bitmap drawing
System.Threading.Thread.Sleep(50);
SizeF size = e.Graphics.MeasureString(text, Font);
e.Graphics.DrawString(text, Font, Brushes.Black, new PointF(Width/2f - size.Width/2f, Height/2f - size.Height/2f));
base.OnPaint(e);
}
}
}
Debug информация:
1) Каждая панель имеет логическое значение поля useUnsafeTime (значение ИСТИНА по умолчанию), что позволяет использовать System.Windows.Forms.Timer (ложь) InstEd из System.Threading.Timer (правда). В первом случае (System.Windows.Forms.Timer) все работает нормально. Удаление System.Threading.Sleep-вызов в OnPaint также делает выполнение штрафом.
2) Установка интервала таймера до 25 мс или менее предотвращает повторную перемотку второй панели (в то время как пользователь не изменяет размер формы).
3) Использование System.Windows.Forms.Timer приводит к скорости increasement
4) Принудительное управление для ввода контекста синхронизации (Invoke) не имеет смысла. Я имею в виду, что Invalidate (invalidateChildren = false) является «потокобезопасным» и может иметь различное поведение в различных контекстах.
5) Ничего интересного в сравнении этих двух таймеров от IL ... Они просто используют разные функции WinAPI для Установка и удаление таймеры (AddTimerNative, DeleteTimerNative для Threading.Timer; SetTimer, KillTimer для Windows.Forms.Timer) и Windows.Forms.Timer использует метод WndProc NativeWindow для поднимающегося Tick события
Я использую аналогичный фрагмент код в моем приложение и, к сожалению, нет способа использования System.Windows.Forms.Timer) Я использую многопоточную многопоточную визуализацию изображений двух панелей и вызывается метод Invalidate после завершения рендеринга на каждой панели ...
Было бы здорово, если бы кто-то помог мне понять, что происходит за кулисами и как решить проблему.
P.S. Интересное поведение не так ли? =)
Я упростил «задачу» ... просто отключите таймеры панели и добавьте эту строку кода в MysticForm ctor новый System.Threading.Timer (state => {Invoke (new MethodInvoker() => {Invalidate (true);}));}, null, 1000, 50); мы не должны видеть ПРОБЛЕМУ, но она все еще существует. не должно быть состояния гонки. Я прав? =) – Mikant
Erm, вы вызываете быстрее, чем поток пользовательского интерфейса может идти в ногу. В конечном итоге вы умрете в OOM. И когда вы закрываете форму, есть еще одна гонка. Есть ли смысл пытаться сделать эту работу? Что вы на самом деле пытаетесь сделать. –
(если я правильно понял вас) У меня есть два больших растровых изображения (1000x500) и вы хотите нарисовать их на двух панелях в один момент (например, 2 фотографии, привязанные по ширине), но определяется время вызова Invalidate в другом потоке ... (например, каждые 50 мс он дает фотографии (новые) случайной ширины) – Mikant