Ho gia trattato l'accesso thread-safe a controlli windows form ma spesso non ci rende conto che ci troviamo in un identica situazione anche utilizzando alcune classi del framework le quali utilizzano threads presi dal threadpoll per gestire la loro funzionalita'.
Prendete ad esempio il timer presente in System.Timers

using System.Timers;

System.Timers.Timer tim=new System.Timers.Timer();
tim.Interval=500;
tim.AutoReset=true
;
tim.Elapsed+=new
ElapsedEventHandler(tim_Elapsed);
tim.Start();

private void tim_Elapsed(object sender, ElapsedEventArgs e)
{
System.Diagnostics.Debug.Assert(label1.InvokeRequired==false
);
label1.Text=DateTime.Now.ToString();
}

Lanciando questo esempio noterete che il debugger si fermera' sulla Debug.Assert in quanto l'evento Elapsed viene servito in un thread diverso.

Per far si che l'evento venga generato nello stesso thread e' possibile associare la proprieta' SynchronizingObject dell'oggetto Timer al controllo che vogliamo utilizzare nell'evento, in questo modo il framework si occupera di effettuare il marshaling della chiamata consentendoci percio' un accesso thread-safe al controllo stesso.

tim.SynchronizingObject=label1;

Da uno sguardo veloce n MSDN appare che le seguenti classi espongono SynchronizingObject:

System.Timers.Timer
FileSystemWatcher
EventLog
Process
MessageQueue