Processing a webpage source
Relying on the max number of thread pool threads to cap the number of concurrent downloads feels ... wrong. It may also cause problems if another component needs a thread pool while all 25 are waiting for a response from web servers.

(slight correction) Although you use semaphores to limit the number of concurrent downloads to ten, you will still have up to 25 thread pool threads in the DoWork() method (15 blocked on the semaphore).

Two alternatives:


Manage you own task list and threads (since 4k URLs per thread could take more than a few seconds).
Make your requests asynchronously and track the list of IAsyncResult objects (though this may get a little fiddly).

I'm not sure I like the idea of using a static Semaphore to protect instance data, you should store the semaphore in a local field. Even if you only ever have a single instance, I prefer to scope the lock similarly to the data to be protected. (sorry, I thought the semaphore was to protect the controls rather than used as a throttle)
You should release your semaphore in a finally block.
Only the thread that created a control should be used to update it. I recommend using ISynchronizeInvoke.Invoke() to call AddItem() (implemented implicitly on Control). This will also remove the need to use a semaphore to protect the control (though you may still need one to protect a pending URL list depending on what you do about the first point). (this is already done, sorry, missed it).
What is the purpose of the 1000 millisecond sleep? it appears before the tasks are even queued.