1. Ну, `List`
не потокобезопасен. Для избежания Kernel-mode constructs (`lock` и пр.) можно попробовать потокобезопасные ConcurrentQueue или ConcurrentStack в
System.Collections.Concurrent, если заполнение до чтения (а не вовремя). Наполняете, вызываете
GetEnumerable, который делает снэпшот коллекции, сравниваете. Чтение должно происходить без блокировки.
2. BlockingCollection для проблемы producer-consumer, когда одновременно чтение и запись. Я так понял это не входит в задачу.
3. Если вы не изменяете, то и блокировать не нужно. Вне зависимости от reference или value type. (Конечно, через стэк лучше, если изменяете. Тогда копия для каждого потока своя.)
По теме можно Рихтера почитать, CLR via C#, 10 глава, Asynchronous Synchronization и The Concurrent Collection Classes