We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Auto reloader consumes full CPU core
**Describe the bug**

Enabling the auto reloader (`auto_reload=True` or `--auto-reload`) will consume a full CPU core, because it is looping over all files in all loaded modules and `stat`-ing every file. 100% usage on a single core is observed on macOS and Linux.

The problematic loop is here:

https://github.com/sanic-org/sanic/blob/f891995b487f01ff1207afcd241ae359725a8e3c/sanic/worker/reloader.py#L46-L64

**Code snippet**

Run the following minimal example with `python3 test.py` or `sanic test:app --auto-reload` and watch CPU usage

```python
from sanic import Sanic, text

app = Sanic("test")


@app.route("/")
def hello_world(request):
    return text("hello, world")


if __name__ == "__main__":
    app.run(auto_reload=True)
```


**Expected behavior**

The reloader should not consume a full CPU core, but should watch the file system for changes more intelligently, using platform-specific methods (inotify, FSEvents, kqueue, etc). See the [watchdog](https://github.com/gorakhargosh/watchdog/) project for inspiration. Maybe `watchdog` could be included as an optional dependency and used if available?

For instance, [another popular framework has implemented two reloader loops](https://github.com/pallets/werkzeug/blob/2.2.2/src/werkzeug/_reloader.py) and will select the `watchdog` loop if the package is available.

Alternatively, simply add a short sleep step to the reloader loop. I think a one second delay in reloading is acceptable.

**Environment (please complete the following information):**
 - OS: macOS 12.6, Ubuntu Linux 22.04 (likely also Windows, but not tested)
 - Sanic Version: 22.9.0


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sanic/worker/reloader.py`
Content:
```
1 from __future__ import annotations
2 
3 import os
4 import sys
5 
6 from asyncio import new_event_loop
7 from itertools import chain
8 from multiprocessing.connection import Connection
9 from pathlib import Path
10 from signal import SIGINT, SIGTERM
11 from signal import signal as signal_func
12 from typing import Dict, Set
13 
14 from sanic.server.events import trigger_events
15 from sanic.worker.loader import AppLoader
16 
17 
18 class Reloader:
19     def __init__(
20         self,
21         publisher: Connection,
22         interval: float,
23         reload_dirs: Set[Path],
24         app_loader: AppLoader,
25     ):
26         self._publisher = publisher
27         self.interval = interval
28         self.reload_dirs = reload_dirs
29         self.run = True
30         self.app_loader = app_loader
31 
32     def __call__(self) -> None:
33         app = self.app_loader.load()
34         signal_func(SIGINT, self.stop)
35         signal_func(SIGTERM, self.stop)
36         mtimes: Dict[str, float] = {}
37 
38         reloader_start = app.listeners.get("reload_process_start")
39         reloader_stop = app.listeners.get("reload_process_stop")
40         before_trigger = app.listeners.get("before_reload_trigger")
41         after_trigger = app.listeners.get("after_reload_trigger")
42         loop = new_event_loop()
43         if reloader_start:
44             trigger_events(reloader_start, loop, app)
45 
46         while self.run:
47             changed = set()
48             for filename in self.files():
49                 try:
50                     if self.check_file(filename, mtimes):
51                         path = (
52                             filename
53                             if isinstance(filename, str)
54                             else filename.resolve()
55                         )
56                         changed.add(str(path))
57                 except OSError:
58                     continue
59             if changed:
60                 if before_trigger:
61                     trigger_events(before_trigger, loop, app)
62                 self.reload(",".join(changed) if changed else "unknown")
63                 if after_trigger:
64                     trigger_events(after_trigger, loop, app)
65         else:
66             if reloader_stop:
67                 trigger_events(reloader_stop, loop, app)
68 
69     def stop(self, *_):
70         self.run = False
71 
72     def reload(self, reloaded_files):
73         message = f"__ALL_PROCESSES__:{reloaded_files}"
74         self._publisher.send(message)
75 
76     def files(self):
77         return chain(
78             self.python_files(),
79             *(d.glob("**/*") for d in self.reload_dirs),
80         )
81 
82     def python_files(self):  # no cov
83         """This iterates over all relevant Python files.
84 
85         It goes through all
86         loaded files from modules, all files in folders of already loaded
87         modules as well as all files reachable through a package.
88         """
89         # The list call is necessary on Python 3 in case the module
90         # dictionary modifies during iteration.
91         for module in list(sys.modules.values()):
92             if module is None:
93                 continue
94             filename = getattr(module, "__file__", None)
95             if filename:
96                 old = None
97                 while not os.path.isfile(filename):
98                     old = filename
99                     filename = os.path.dirname(filename)
100                     if filename == old:
101                         break
102                 else:
103                     if filename[-4:] in (".pyc", ".pyo"):
104                         filename = filename[:-1]
105                     yield filename
106 
107     @staticmethod
108     def check_file(filename, mtimes) -> bool:
109         need_reload = False
110 
111         mtime = os.stat(filename).st_mtime
112         old_time = mtimes.get(filename)
113         if old_time is None:
114             mtimes[filename] = mtime
115         elif mtime > old_time:
116             mtimes[filename] = mtime
117             need_reload = True
118 
119         return need_reload
120
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sanic/worker/reloader.py b/sanic/worker/reloader.py
--- a/sanic/worker/reloader.py
+++ b/sanic/worker/reloader.py
@@ -9,6 +9,7 @@
 from pathlib import Path
 from signal import SIGINT, SIGTERM
 from signal import signal as signal_func
+from time import sleep
 from typing import Dict, Set
 
 from sanic.server.events import trigger_events
@@ -62,6 +63,7 @@
                 self.reload(",".join(changed) if changed else "unknown")
                 if after_trigger:
                     trigger_events(after_trigger, loop, app)
+            sleep(self.interval)
         else:
             if reloader_stop:
                 trigger_events(reloader_stop, loop, app)

{"golden_diff": "diff --git a/sanic/worker/reloader.py b/sanic/worker/reloader.py\n--- a/sanic/worker/reloader.py\n+++ b/sanic/worker/reloader.py\n@@ -9,6 +9,7 @@\n from pathlib import Path\n from signal import SIGINT, SIGTERM\n from signal import signal as signal_func\n+from time import sleep\n from typing import Dict, Set\n \n from sanic.server.events import trigger_events\n@@ -62,6 +63,7 @@\n                 self.reload(\",\".join(changed) if changed else \"unknown\")\n                 if after_trigger:\n                     trigger_events(after_trigger, loop, app)\n+            sleep(self.interval)\n         else:\n             if reloader_stop:\n                 trigger_events(reloader_stop, loop, app)\n", "issue": "Auto reloader consumes full CPU core\n**Describe the bug**\r\n\r\nEnabling the auto reloader (`auto_reload=True` or `--auto-reload`) will consume a full CPU core, because it is looping over all files in all loaded modules and `stat`-ing every file. 100% usage on a single core is observed on macOS and Linux.\r\n\r\nThe problematic loop is here:\r\n\r\nhttps://github.com/sanic-org/sanic/blob/f891995b487f01ff1207afcd241ae359725a8e3c/sanic/worker/reloader.py#L46-L64\r\n\r\n**Code snippet**\r\n\r\nRun the following minimal example with `python3 test.py` or `sanic test:app --auto-reload` and watch CPU usage\r\n\r\n```python\r\nfrom sanic import Sanic, text\r\n\r\napp = Sanic(\"test\")\r\n\r\n\r\n@app.route(\"/\")\r\ndef hello_world(request):\r\n    return text(\"hello, world\")\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    app.run(auto_reload=True)\r\n```\r\n\r\n\r\n**Expected behavior**\r\n\r\nThe reloader should not consume a full CPU core, but should watch the file system for changes more intelligently, using platform-specific methods (inotify, FSEvents, kqueue, etc). See the [watchdog](https://github.com/gorakhargosh/watchdog/) project for inspiration. Maybe `watchdog` could be included as an optional dependency and used if available?\r\n\r\nFor instance, [another popular framework has implemented two reloader loops](https://github.com/pallets/werkzeug/blob/2.2.2/src/werkzeug/_reloader.py) and will select the `watchdog` loop if the package is available.\r\n\r\nAlternatively, simply add a short sleep step to the reloader loop. I think a one second delay in reloading is acceptable.\r\n\r\n**Environment (please complete the following information):**\r\n - OS: macOS 12.6, Ubuntu Linux 22.04 (likely also Windows, but not tested)\r\n - Sanic Version: 22.9.0\r\n\n", "before_files": [{"content": "from __future__ import annotations\n\nimport os\nimport sys\n\nfrom asyncio import new_event_loop\nfrom itertools import chain\nfrom multiprocessing.connection import Connection\nfrom pathlib import Path\nfrom signal import SIGINT, SIGTERM\nfrom signal import signal as signal_func\nfrom typing import Dict, Set\n\nfrom sanic.server.events import trigger_events\nfrom sanic.worker.loader import AppLoader\n\n\nclass Reloader:\n    def __init__(\n        self,\n        publisher: Connection,\n        interval: float,\n        reload_dirs: Set[Path],\n        app_loader: AppLoader,\n    ):\n        self._publisher = publisher\n        self.interval = interval\n        self.reload_dirs = reload_dirs\n        self.run = True\n        self.app_loader = app_loader\n\n    def __call__(self) -> None:\n        app = self.app_loader.load()\n        signal_func(SIGINT, self.stop)\n        signal_func(SIGTERM, self.stop)\n        mtimes: Dict[str, float] = {}\n\n        reloader_start = app.listeners.get(\"reload_process_start\")\n        reloader_stop = app.listeners.get(\"reload_process_stop\")\n        before_trigger = app.listeners.get(\"before_reload_trigger\")\n        after_trigger = app.listeners.get(\"after_reload_trigger\")\n        loop = new_event_loop()\n        if reloader_start:\n            trigger_events(reloader_start, loop, app)\n\n        while self.run:\n            changed = set()\n            for filename in self.files():\n                try:\n                    if self.check_file(filename, mtimes):\n                        path = (\n                            filename\n                            if isinstance(filename, str)\n                            else filename.resolve()\n                        )\n                        changed.add(str(path))\n                except OSError:\n                    continue\n            if changed:\n                if before_trigger:\n                    trigger_events(before_trigger, loop, app)\n                self.reload(\",\".join(changed) if changed else \"unknown\")\n                if after_trigger:\n                    trigger_events(after_trigger, loop, app)\n        else:\n            if reloader_stop:\n                trigger_events(reloader_stop, loop, app)\n\n    def stop(self, *_):\n        self.run = False\n\n    def reload(self, reloaded_files):\n        message = f\"__ALL_PROCESSES__:{reloaded_files}\"\n        self._publisher.send(message)\n\n    def files(self):\n        return chain(\n            self.python_files(),\n            *(d.glob(\"**/*\") for d in self.reload_dirs),\n        )\n\n    def python_files(self):  # no cov\n        \"\"\"This iterates over all relevant Python files.\n\n        It goes through all\n        loaded files from modules, all files in folders of already loaded\n        modules as well as all files reachable through a package.\n        \"\"\"\n        # The list call is necessary on Python 3 in case the module\n        # dictionary modifies during iteration.\n        for module in list(sys.modules.values()):\n            if module is None:\n                continue\n            filename = getattr(module, \"__file__\", None)\n            if filename:\n                old = None\n                while not os.path.isfile(filename):\n                    old = filename\n                    filename = os.path.dirname(filename)\n                    if filename == old:\n                        break\n                else:\n                    if filename[-4:] in (\".pyc\", \".pyo\"):\n                        filename = filename[:-1]\n                    yield filename\n\n    @staticmethod\n    def check_file(filename, mtimes) -> bool:\n        need_reload = False\n\n        mtime = os.stat(filename).st_mtime\n        old_time = mtimes.get(filename)\n        if old_time is None:\n            mtimes[filename] = mtime\n        elif mtime > old_time:\n            mtimes[filename] = mtime\n            need_reload = True\n\n        return need_reload\n", "path": "sanic/worker/reloader.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport os\nimport sys\n\nfrom asyncio import new_event_loop\nfrom itertools import chain\nfrom multiprocessing.connection import Connection\nfrom pathlib import Path\nfrom signal import SIGINT, SIGTERM\nfrom signal import signal as signal_func\nfrom time import sleep\nfrom typing import Dict, Set\n\nfrom sanic.server.events import trigger_events\nfrom sanic.worker.loader import AppLoader\n\n\nclass Reloader:\n    def __init__(\n        self,\n        publisher: Connection,\n        interval: float,\n        reload_dirs: Set[Path],\n        app_loader: AppLoader,\n    ):\n        self._publisher = publisher\n        self.interval = interval\n        self.reload_dirs = reload_dirs\n        self.run = True\n        self.app_loader = app_loader\n\n    def __call__(self) -> None:\n        app = self.app_loader.load()\n        signal_func(SIGINT, self.stop)\n        signal_func(SIGTERM, self.stop)\n        mtimes: Dict[str, float] = {}\n\n        reloader_start = app.listeners.get(\"reload_process_start\")\n        reloader_stop = app.listeners.get(\"reload_process_stop\")\n        before_trigger = app.listeners.get(\"before_reload_trigger\")\n        after_trigger = app.listeners.get(\"after_reload_trigger\")\n        loop = new_event_loop()\n        if reloader_start:\n            trigger_events(reloader_start, loop, app)\n\n        while self.run:\n            changed = set()\n            for filename in self.files():\n                try:\n                    if self.check_file(filename, mtimes):\n                        path = (\n                            filename\n                            if isinstance(filename, str)\n                            else filename.resolve()\n                        )\n                        changed.add(str(path))\n                except OSError:\n                    continue\n            if changed:\n                if before_trigger:\n                    trigger_events(before_trigger, loop, app)\n                self.reload(\",\".join(changed) if changed else \"unknown\")\n                if after_trigger:\n                    trigger_events(after_trigger, loop, app)\n            sleep(self.interval)\n        else:\n            if reloader_stop:\n                trigger_events(reloader_stop, loop, app)\n\n    def stop(self, *_):\n        self.run = False\n\n    def reload(self, reloaded_files):\n        message = f\"__ALL_PROCESSES__:{reloaded_files}\"\n        self._publisher.send(message)\n\n    def files(self):\n        return chain(\n            self.python_files(),\n            *(d.glob(\"**/*\") for d in self.reload_dirs),\n        )\n\n    def python_files(self):  # no cov\n        \"\"\"This iterates over all relevant Python files.\n\n        It goes through all\n        loaded files from modules, all files in folders of already loaded\n        modules as well as all files reachable through a package.\n        \"\"\"\n        # The list call is necessary on Python 3 in case the module\n        # dictionary modifies during iteration.\n        for module in list(sys.modules.values()):\n            if module is None:\n                continue\n            filename = getattr(module, \"__file__\", None)\n            if filename:\n                old = None\n                while not os.path.isfile(filename):\n                    old = filename\n                    filename = os.path.dirname(filename)\n                    if filename == old:\n                        break\n                else:\n                    if filename[-4:] in (\".pyc\", \".pyo\"):\n                        filename = filename[:-1]\n                    yield filename\n\n    @staticmethod\n    def check_file(filename, mtimes) -> bool:\n        need_reload = False\n\n        mtime = os.stat(filename).st_mtime\n        old_time = mtimes.get(filename)\n        if old_time is None:\n            mtimes[filename] = mtime\n        elif mtime > old_time:\n            mtimes[filename] = mtime\n            need_reload = True\n\n        return need_reload\n", "path": "sanic/worker/reloader.py"}]}