Я столкнулся с тем, чего я просто не понимаю. В моем приложении есть несколько потоков, все они добавляют (и удаляют) элементы в общую коллекцию (используя общую блокировку). Поток 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 вызывался из блокировки.
Спасибо.