Race Condition in session.adapters: OrderedDict mutated during iteration

## Expected Result

This race condition happens between `session.mount()` and `session.get_adapter()`. If one thread calls `session.mount()` while `session.get_adapter()` is executing, a `RuntimeError` will be thrown. The proposed fix, in order to make it thread-safe, is to use a `threading.Lock()` when accessing or iterating `session.adapters`.

## Actual Result

```python
Traceback (most recent call last):
  File "/home/user/requests_bug.py", line 23, in <module>
    result = future.result()
  File "/usr/lib/python3.10/concurrent/futures/_base.py", line 451, in result
    return self.__get_result()
  File "/usr/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result
    raise self._exception
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/user/requests_bug.py", line 15, in get_url
    response = session.request("GET", url)
  File "/home/user/.local/lib/python3.10/site-packages/requests/sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/user/.local/lib/python3.10/site-packages/requests/sessions.py", line 695, in send
    adapter = self.get_adapter(url=request.url)
  File "/home/user/.local/lib/python3.10/site-packages/requests/sessions.py", line 786, in get_adapter
    for (prefix, adapter) in self.adapters.items():
RuntimeError: OrderedDict mutated during iteration
```

## Reproduction Steps

This is reproducible using the following script (against the local `python -m http.server`)

```python
import random
import requests
from requests.adapters import HTTPAdapter
from concurrent.futures import ThreadPoolExecutor, as_completed

session = requests.Session()
retry_adapter = HTTPAdapter(max_retries=0)

def get_url(url):
    session.mount(f"{random.randint(0,9999)}://", retry_adapter)
    response = session.request("GET", url)

futures = []
with ThreadPoolExecutor(max_workers=100) as executor:
    for i in range(10000):
        futures.append(executor.submit(get_url, "http://127.0.0.1:8000"))

    for future in as_completed(futures):
        result = future.result()
```

## System Information

```json
{
  "chardet": {
    "version": "3.0.4"
  },
  "charset_normalizer": {
    "version": "2.0.11"
  },
  "cryptography": {
    "version": "38.0.1"
  },
  "idna": {
    "version": "3.4"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.10.9"
  },
  "platform": {
    "release": "6.2.1-arch1-1",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "30000050",
    "version": "22.0.0"
  },
  "requests": {
    "version": "2.28.2"
  },
  "system_ssl": {
    "version": "30000080"
  },
  "urllib3": {
    "version": "1.26.8"
  },
  "using_charset_normalizer": false,
  "using_pyopenssl": true
}
```


Requests does it's level best to be thread safe but this is not a common or even reasonable use of the library in a threaded context. We won't be adding a lock here. If you want this to be thread safe without having to change your usage, I suggest using a thread safe dictionary implementation instead of the ordered dictionary 

@sigmavirus24  Agreed that the above example is wildly unrealistic. But it is designed to reliably coax out a bug that we are seeing in production code -- yes, the chance of it happening is very small, but it still makes appearances. Because it's a race condition it only happens once in a blue moon, but I don't see that as a reason not to fix it. If I submit a pull request, will it be considered?

It will not be considered. Threading Locks will likely significantly harm 99.9% of people's experience using Requests. 

I see your point; threading locks can be troublesome when not used correctly. But in this specific case, it seems to me that due to the extremely small chance of this bug triggering, implementing a lock for the associated object would have virtually no impact on performance. The only time a performance impact would be introduced would be when there is concurrent access on the object, i.e. when the risk of triggering the race condition is highest.



I'm seeing this in production code too, though "once in a blue moon" is likely an accurate estimation of how rare this is.

If this won't be solved in `requests` by default, I assume the recommendation might be to make the code interacting with `session.mount()` to be thread-safe instead?