I think it’s worth giving another example, this time how to use the ISyncronizeInvoke interface. This interface is implemented in the System.Windows.Forms.Control class and is designed specifically for pushing any delegates into the message loop processing flow, in the case of controls, the Windows message loop.
The interface contains 4 members.
The InvokeRequired property returns true if the current execution thread is not a message loop processing thread. If we are already in the message loop processing thread, the property returns false.
The Invoke method adds the delegate passed to it to the message processing cycle and blocks the current thread until this delegate executes the message processing cycle. After the delegate is executed, the method returns control with the value that the delegate passed to it returned during execution. If the delegate had a return type of void, the method returns null.
The BeginInvoke method adds the delegate passed to it as a parameter to the message processing loop and returns immediately. It returns an object of type IAsyncResult, which can be passed as a parameter to the last EndInvoke method to wait for the completion of an asynchronous operation. The simplest message loop, which I used in the previous example, handles events asynchronously, returning control, without waiting for the method added to the queue.
In order to add an element to the collection so that the addition itself necessarily takes place in the UI thread, regardless of where it was called from, you should use this pattern:
private readonly ISynchronizeInvoke m_invoker; private readonly BindingList<MyEntry> m_entries = new BindingList<MyEntry>(); // Конструктор с Dependency Injection, которому передаётся контрол либо форма public BackgroundHandler(ISynchronizeInvoke invoker) { if (invoker == null) throew new ArgumentNullException("invoker"); m_invoker = invoker; } // public bool Add(MyEntry entry) { // Если мы не в потоке обработки сообщений, // метод добавляет в очередь сообщений ссылку на самого себя, // ждёт завершения обработки и возвращает управление if (m_invoker.InvokeRequired) return (bool)invoker.Invoke(new Func<MyEntry, bool>(this.Add), new object[] { entry }); // Поскольку мы сюда попали, мы уже в потоке обработки сообщений, // делаем полезную работу - добавляем переданный элемент в список. // Возвращаемое значение - чисто для демонстрации, как его возвращать m_entries.Add(entry); return true; }
Placing the synchronization code directly to the beginning of the method before the useful work makes it very easy to read the code if the useful work contains a lot of logic.