The common language runtime (CLR) uses a thread pool to listen for requests for remotable objects. That means that if more than one user attempts to call a method at the same time, the tasks will complete simultaneously on different threads. With a sufficiently high number of simultaneous requests, every thread in the pool will be busy, and some users will have to wait for a thread to become free before their task can be completed (or, in extreme cases, until a time-out occurs and an error is raised on the client). With thread pools, threads are kept alive indefinitely and reused as long as there's outstanding work to complete.
Generally, thread pools ensure optimum performance. For the most part, clients won't need to wait (as they probably would if the server provided only one thread). On the other hand, no matter how heavy the traffic, the CLR will never create so many threads that the server computer becomes swamped and unable to complete any of the work before a time-out occurs (which could occur if a new thread was created to serve each and every request).
Multithreaded systems always add a few new wrinkles for the application programmer to worry about. These mainly come in the form of concurrency errors. If these problems aren't anticipated, they can be fiendishly difficult to diagnose and resolve once the application is deployed in the field.
Concurrency errors occur when more than one thread modifies the same piece of memory. The problem is that the last update always takes precedence, even if it doesn't take into account the work performed by other threads. The canonical example of a concurrency error is a global counter that's being incremented by several threads. A concurrency error can occur if all threads attempt to increment the counter at once.
For example, consider the case in which the global counter is currently at 5 and there are two threads at work. Here's how it might unfold for the worse:
Thread A reads the value 5.
Thread B reads the value 5.
Thread A increments the value of the counter to 6.
Thread B increments the value of the counter to 6.
In this case, the last update wins and the counter stands at 6, even though it should really be set to 7 to represent both of the increment operations. This is just one example of a concurrency problem.