Почему ввод блокировки в потоке UI вызывает событие OnPaint?

Я столкнулся с тем, чего я просто не понимаю. В моем приложении есть несколько потоков, все они добавляют (и удаляют) элементы в общую коллекцию (используя общую блокировку). Поток UI использует таймер, и при каждом тике он использует коллекцию для обновления своего UI.

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

public void GUIRefresh()
{
    ///...
    List<Item> tmpList;
    lock (Locker)
    {
         tmpList = SharedList.ToList();
    }
    // Update the datagrid using the tmp list.
}

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

....
at System.Windows.Forms.DataGrid.OnPaint(PaintEventArgs pe)
at MyDataGrid.OnPaint(PaintEventArgs pe)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Threading.Monitor.Enter(Object obj)
at MyApplication.GuiRefresh()   
at System.Windows.Forms.Timer.OnTick(EventArgs e)
at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
....

Обратите внимание, что за вводом блокировки (Monitor.Enter) следует NativeWindow.Callback, который приводит к OnPaint.

  • Как такое возможно? Неужели поток пользовательского интерфейса перехватывается, чтобы проверить свой насос сообщений? Имеет ли это смысл? Или здесь что-то другое?

  • Есть ли способ избежать этого? Я не хочу, чтобы OnPaint вызывался из блокировки.

Спасибо.

9
задан tzachs 8 December 2011 в 12:48
поделиться