Well you shouldn't directly update the UI thread from the background code, but you can certainly consider updating some shared piece of memory to communicate progress.
As a really simple example, if your code can be treated as a series of repeated steps, you can just increment a counter. The Interlocked.Increment()
method is particularly helpful for this purpose, as it does so without requiring a lock. Your UI thread could then periodically poll this counter and report it's value - or visualize it if possible (as perhaps a progress bar or running average).
If multiple machines are involved, it may make sense to keep a counter per machine and report on how the overall task is progressing across participating peers. You could also consider visualizing the average elapsed time between completed steps ... which may help users of the system estimate the time to completion (and you could perhaps compute this as well).
More complex implementation are also possible. You could, for instance, creating a shared queue to which you post logging information or per-activity statuses. This can get pretty complicated ... and if implemented poorly could have an adverse affect on performance.
Alternatively, if you want to collect a significant number of performance metrics about your distributed process, you could consider using the Performance Counters API in System.Diagnostics. This is a more complicated path - but it allows you to leverage the high-performance metric collection and publishing implementation in the OS and allows you to observe and aggregate performance information using a tool like Perfmon.
The specific mechanisms you use to update the UI depend on what technology you are using (WPF, WinForms, HTML) and whether the distributed code and the UI code have any components co-located within a single process. If the distributed/UI portions are not hosted within a single process, you will likely need to use some form of IPC to communicate and visualize progress.