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