Add MaxQueueLength property to support bounded queues

Added a configuration property (`MaxQueueLength`) that can be used to enforce an upper-bound on the size of the `_workItemsQueue`.

If the property value is `null` (default), no upper bound is enforced.

If the property value is `0`, items will be rejected instead of queued when the pool is at its maximum thread count and all threads are working on items.

Queue limits configured on a pool will be ignored by `WorkItemsGroup`s that use that pool (i.e. limits are only applied to `QueueWorkItem()` calls directly on `SmartThreadPool` instances).

When checking the queue limit and the queue length == the configured limit, items won't be rejected if the pool is going to immediately scale up (i.e. it hasn't reached its max threads). This means the queue limit bends a little bit (and is essentially the configured limit + potential available threads).