Repository: dask/dask

Issue:
`dask.config.set(num_workers=n)` context doesn't seem to work anymore
I don't know how to make a self contained example, but suffice to say that if I try something like

```python
with dask.config.set(num_workers=2):
    # code the does something
```

All cores start firing, not just 2.

```
%watermark -v -m -p dask
CPython 3.6.6
IPython 7.0.1

dask 0.19.3

compiler   : MSC v.1900 64 bit (AMD64)
system     : Windows
release    : 10
machine    : AMD64
processor  : Intel64 Family 6 Model 63 Stepping 2, GenuineIntel
CPU cores  : 48
interpreter: 64bit
```



Discussion:
Thanks for the issue report @david-hoffman. I think I am able to reproduce this using

```python
import dask
import dask.array as da
from dask.diagnostics import visualize, Profiler

a = da.random.randint(0, 100, size=100_000, chunks=1_000)

with dask.config.set(num_workers=2) as _, Profiler() as prof:
    a.compute()
    visualize([prof])
```

The visualizing the `Profiler` yields

<img width="825" alt="screen shot 2018-10-11 at 6 26 48 pm" src="https://user-images.githubusercontent.com/11656932/46839426-7577de80-cd83-11e8-9785-91e34f715c53.png">

So it looks like the `compute()` call is being executed with 4 workers instead of 2. 

cc @mrocklin 


Resolving patch:
diff --git a/dask/multiprocessing.py b/dask/multiprocessing.py
--- a/dask/multiprocessing.py
+++ b/dask/multiprocessing.py
@@ -160,6 +160,7 @@ def get(dsk, keys, num_workers=None, func_loads=None, func_dumps=None,
         If True [default], `fuse` is applied to the graph before computation.
     """
     pool = config.get('pool', None)
+    num_workers = num_workers or config.get('num_workers', None)
     if pool is None:
         context = get_context()
         pool = context.Pool(num_workers,
diff --git a/dask/threaded.py b/dask/threaded.py
--- a/dask/threaded.py
+++ b/dask/threaded.py
@@ -56,6 +56,7 @@ def get(dsk, result, cache=None, num_workers=None, **kwargs):
     """
     global default_pool
     pool = config.get('pool', None)
+    num_workers = num_workers or config.get('num_workers', None)
     thread = current_thread()
 
     with pools_lock:


Test patch:
diff --git a/dask/tests/test_base.py b/dask/tests/test_base.py
--- a/dask/tests/test_base.py
+++ b/dask/tests/test_base.py
@@ -19,6 +19,7 @@
 from dask.utils import tmpdir, tmpfile, ignoring
 from dask.utils_test import inc, dec
 from dask.compatibility import long, unicode, PY2
+from dask.diagnostics import Profiler
 
 
 def import_or_none(path):
@@ -868,3 +869,18 @@ def get(dsk, keys, *args, **kwargs):
 
     assert delayed(lambda: 1)().compute(scheduler=get) == 1
     assert called[0]
+
+
+@pytest.mark.skipif('not da')
+@pytest.mark.parametrize('num_workers', range(1, 4))
+@pytest.mark.parametrize('scheduler', ['threads', 'processes'])
+def test_num_workers_config(num_workers, scheduler):
+    # Regression test for issue #4082
+    a = da.random.randint(0, 10, size=400, chunks=2)
+
+    with dask.config.set(num_workers=num_workers), Profiler() as prof:
+        a.compute(scheduler=scheduler)
+
+    workers = {i.worker_id for i in prof.results}
+
+    assert len(workers) == num_workers