Repository: locustio/locust

Issue:
"New test" with different number of users
It seems likes a bug to me, but please correct me if I'm wrong. It seems that after clicking on "new test" and giving lower amount of users to be created, Locust still uses some of the users created on first run.

### Describe the bug
Example:
When i start locust and create (total) 5 users with 5 users per sec, I get correct amount of users/requests. That is correct. Then i click Stop (in the UI).
After clicking on "New test" and specifying lower amount of users(total 1 and 1 per sec), Locust still starts 4 users in total and execute tasks - but in console i can see that 1 user was spawned

Code:
```
import logging
import time
import uuid
from locust import task, HttpUser, SequentialTaskSet, constant_pacing, task, events
from locust.exception import StopUser

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("locust")

class UserTasks(SequentialTaskSet):
    wait_time = constant_pacing(1)

    @task
    def test_api_one(self):
        logger.info(f"Task1: {self.parent.user_id}")
        time.sleep(1)

    @task
    def test_api_two(self):
        logger.info(f"Task2: {self.parent.user_id}")
        time.sleep(1)
    
    @task
    def test_api_three(self):
        self.interrupt(False)

class WebsiteUser(HttpUser):
    host = "http://local"
    wait_time = constant_pacing(5)
    tasks = [UserTasks]

    def on_start(self):
        self.user_id = str(uuid.uuid4())
        logger.info(f"START USER: {self.user_id}")

    def on_stop(self):
        logger.info(f"STOP USER: {self.user_id}")
```

### Expected behaviour
I would expect that if i run new test with lower number of users - from 5 to 1 users, Locust will just create 1 user and run (spawn) only that one.

You can see it on the screenshot:


### Environment

- OS: docker with python 3.9
- Python version: 3.9
- Locust 2.9.0
- locust -f temp.py

![Screenshot 2022-07-20 at 16 21 47](https://user-images.githubusercontent.com/83775196/180020920-6ceb807a-26b3-4de6-b849-e65629ce54fb.png)




Discussion:
Wow, that looks crazy. Can you update to latest and see if it makes a difference?
Hi.
I got the same results on latest (v.2.10.1)
Any difference if you remove `test_api_three`?
this was added because of the logic we try to test but even when i remove it unfortunately still starts 4 users instead of 1 :/

screenshot:
![Screenshot 2022-07-21 at 13 18 13](https://user-images.githubusercontent.com/83775196/180211851-1d42ca78-f993-4468-a15d-ae83b8055b2c.png)


Very strange. I’ll take a look. 
Getting the exact same behavior. Additionally, all the users from the previous test are spawned instantaneously and don't respect the chosen "Spawn rate".
Damn, I dropped the ball here. Will pick it up some time next week if nobody else has the time. Maybe @mboutet ?

Resolving patch:
diff --git a/locust/runners.py b/locust/runners.py
--- a/locust/runners.py
+++ b/locust/runners.py
@@ -407,6 +407,8 @@ def stop(self) -> None:
 
         self.stop_users(self.user_classes_count)
 
+        self._users_dispatcher = None
+
         self.update_state(STATE_STOPPED)
 
         self.cpu_log_warning()


Test patch:
diff --git a/locust/test/test_runners.py b/locust/test/test_runners.py
--- a/locust/test/test_runners.py
+++ b/locust/test/test_runners.py
@@ -768,6 +768,61 @@ class BaseUser2(BaseUser1):
         sleep(1)
         self.assertEqual(0, runner.user_count)
 
+    def test_user_count_starts_from_specified_amount_when_creating_new_test_after_previous_step_has_been_stopped(self):
+        """Test for https://github.com/locustio/locust/issues/2135"""
+
+        class MyUser1(User):
+            wait_time = constant(0)
+
+            @task
+            def my_task(self):
+                pass
+
+        env = Environment(user_classes=[MyUser1], stop_timeout=0)
+        local_runner = env.create_local_runner()
+        web_ui = env.create_web_ui("127.0.0.1", 0)
+
+        gevent.sleep(0.1)
+
+        response = requests.post(
+            f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
+            data={"user_count": 20, "spawn_rate": 20, "host": "https://localhost"},
+        )
+        self.assertEqual(200, response.status_code)
+
+        t0 = time.perf_counter()
+        while local_runner.user_count != 20:
+            self.assertTrue(time.perf_counter() - t0 <= 1, local_runner.user_count)
+            gevent.sleep(0.1)
+
+        response = requests.get(
+            f"http://127.0.0.1:{web_ui.server.server_port}/stop",
+        )
+        self.assertEqual(200, response.status_code)
+
+        t0 = time.perf_counter()
+        while local_runner.state != STATE_STOPPED:
+            self.assertTrue(time.perf_counter() - t0 <= 1, local_runner.state)
+            gevent.sleep(0.1)
+
+        t0 = time.perf_counter()
+        response = requests.post(
+            f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
+            data={"user_count": 1, "spawn_rate": 1, "host": "https://localhost"},
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertTrue(time.perf_counter() - t0 <= 1, "Stop endpoint should not be blocking")
+
+        # Make sure user count stays at 1 over 2s (2s is arbitrary, but we
+        # wait long enough to make sure it stays constant)
+        t0 = time.perf_counter()
+        while time.perf_counter() - t0 <= 2:
+            self.assertTrue(local_runner.user_count <= 1, local_runner.user_count)
+            gevent.sleep(0.1)
+
+        local_runner.stop()
+        web_ui.stop()
+
 
 class TestMasterWorkerRunners(LocustTestCase):
     def test_distributed_integration_run(self):