diff --git a/dask/base.py b/dask/base.py
--- a/dask/base.py
+++ b/dask/base.py
@@ -1061,12 +1061,19 @@ def _normalize_function(func: Callable) -> tuple | str | bytes:
                 return result
         except Exception:
             pass
-        try:
-            import cloudpickle
+        if not config.get("tokenize.ensure-deterministic"):
+            try:
+                import cloudpickle
 
-            return cloudpickle.dumps(func, protocol=4)
-        except Exception:
-            return str(func)
+                return cloudpickle.dumps(func, protocol=4)
+            except Exception:
+                return str(func)
+        else:
+            raise RuntimeError(
+                f"Function {str(func)} may not be deterministically hashed by "
+                "cloudpickle. See: https://github.com/cloudpipe/cloudpickle/issues/385 "
+                "for more information."
+            )
 
 
 def normalize_dataclass(obj):

@LunarLanding Welcome to Dask, and thanks for reporting!
I took a look at this with @ian-r-rose, and this seems to be because cloudpickle isn't always deterministic: https://github.com/cloudpipe/cloudpickle/issues/385

I agree we can raise a warning/runtime error here. I can open a PR. :)

Reproducer:

```python
import dask.config
from dask.base import tokenize

a, b = (lambda a: a, lambda a: a)
tokenize(a) == tokenize(b) # True

x, y = (lambda a: a,
lambda a: a) # Just add a new-line
tokenize(x) == tokenize(y) # False
```

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
@@ -255,6 +255,16 @@ def test_tokenize_object():
             normalize_token(o)
 
 
+def test_tokenize_function_cloudpickle():
+    a, b = (lambda x: x, lambda x: x)
+    # No error by default
+    tokenize(a)
+
+    with dask.config.set({"tokenize.ensure-deterministic": True}):
+        with pytest.raises(RuntimeError, match="may not be deterministically hashed"):
+            tokenize(b)
+
+
 def test_tokenize_callable():
     def my_func(a, b, c=1):
         return a + b + c

normalize_token does not raise warning for function with non-deterministic hash
```
import dask.config
from dask.base import tokenize
a,b = (
    lambda a: a,
    lambda a: a,
)
with dask.config.set({"tokenize.ensure-deterministic":True}):
    print(tokenize(a)==tokenize(b))
str(a),str(b)
```
Gives:
```
False
('<function <lambda> at 0x14a0d079fee0>',
 '<function <lambda> at 0x14a0ca90cc10>')
```
This is because of the last lines here, where the code gives up by using `str(func)`: 
https://github.com/dask/dask/blob/c60b1f757a3b92361504833cf0417e9c77b82514/dask/base.py#L1043-L1069

dask/tests/test_base.py::test_tokenize_function_cloudpickle
dask/tests/test_base.py::test_normalize_function_dataclass_field_no_repr dask/tests/test_base.py::test_tokenize_object dask/tests/test_base.py::test_tokenize_datetime_date dask/tests/test_base.py::test_tokenize_numpy_array_on_object_dtype dask/tests/test_base.py::test_custom_collection dask/tests/test_base.py::test_tokenize_numpy_scalar dask/tests/test_base.py::test_tokenize_numpy_memmap dask/tests/test_base.py::test_tokenize_dense_sparse_array[dia] dask/tests/test_base.py::test_tokenize_base_types[1j0] dask/tests/test_base.py::test_persist_delayed dask/tests/test_base.py::test_tokenize_range dask/tests/test_base.py::test_compute_with_literal dask/tests/test_base.py::test_tokenize_numpy_scalar_string_rep dask/tests/test_base.py::test_persist_delayed_custom_key[a] dask/tests/test_base.py::test_optimize_globals dask/tests/test_base.py::test_get_collection_names dask/tests/test_base.py::test_tokenize_dense_sparse_array[coo] dask/tests/test_base.py::test_optimizations_keyword dask/tests/test_base.py::test_persist_array_bag dask/tests/test_base.py::test_compute_array_bag dask/tests/test_base.py::test_visualize_highlevelgraph dask/tests/test_base.py::test_tokenize_base_types[1j1] dask/tests/test_base.py::test_compute_as_if_collection_low_level_task_graph dask/tests/test_base.py::test_get_name_from_key dask/tests/test_base.py::test_persist_array dask/tests/test_base.py::test_persist_item_change_name dask/tests/test_base.py::test_tokenize_numpy_array_supports_uneven_sizes dask/tests/test_base.py::test_persist_delayed_rename[a-rename2-b] dask/tests/test_base.py::test_tokenize_base_types[a0] dask/tests/test_base.py::test_tokenize_numpy_memmap_no_filename dask/tests/test_base.py::test_tokenize_dense_sparse_array[csr] dask/tests/test_base.py::test_tokenize_numpy_array_consistent_on_values dask/tests/test_base.py::test_persist_delayed_custom_key[key1] dask/tests/test_base.py::test_persist_delayedattr dask/tests/test_base.py::test_scheduler_keyword dask/tests/test_base.py::test_persist_array_rename dask/tests/test_base.py::test_tokenize_base_types[x8] dask/tests/test_base.py::test_get_scheduler dask/tests/test_base.py::test_get_scheduler_with_distributed_active dask/tests/test_base.py::test_callable_scheduler dask/tests/test_base.py::test_tokenize_dict dask/tests/test_base.py::test_persist_bag dask/tests/test_base.py::test_tokenize_numpy_memmap_offset dask/tests/test_base.py::test_tokenize_numpy_ufunc_consistent dask/tests/test_base.py::test_tokenize_base_types[str] dask/tests/test_base.py::test_tokenize_pandas_index dask/tests/test_base.py::test_default_imports dask/tests/test_base.py::test_tokenize_dense_sparse_array[csc] dask/tests/test_base.py::test_tokenize_set dask/tests/test_base.py::test_persist_item dask/tests/test_base.py::test_is_dask_collection dask/tests/test_base.py::test_tokenize_base_types[1.0] dask/tests/test_base.py::test_raise_get_keyword dask/tests/test_base.py::test_normalize_function dask/tests/test_base.py::test_optimizations_ctd dask/tests/test_base.py::test_tokenize_dataclass dask/tests/test_base.py::test_tokenize_dense_sparse_array[bsr] dask/tests/test_base.py::test_tokenize_numpy_matrix dask/tests/test_base.py::test_tokenize_base_types[True] dask/tests/test_base.py::test_optimize dask/tests/test_base.py::test_normalize_function_limited_size dask/tests/test_base.py::test_unpack_collections dask/tests/test_base.py::test_tokenize_base_types[x9] dask/tests/test_base.py::test_tokenize_sequences dask/tests/test_base.py::test_tokenize_dense_sparse_array[lil] dask/tests/test_base.py::test_compute_nested dask/tests/test_base.py::test_tokenize_base_types[1] dask/tests/test_base.py::test_persist_nested dask/tests/test_base.py::test_persist_literals dask/tests/test_base.py::test_tokenize_base_types[x7] dask/tests/test_base.py::test_normalize_base dask/tests/test_base.py::test_visualize dask/tests/test_base.py::test_optimize_None dask/tests/test_base.py::test_persist_bag_rename dask/tests/test_base.py::test_tokenize_partial_func_args_kwargs_consistent dask/tests/test_base.py::test_tokenize_ordered_dict dask/tests/test_base.py::test_tokenize_object_array_with_nans dask/tests/test_base.py::test_persist_delayedleaf dask/tests/test_base.py::test_compute_no_opt dask/tests/test_base.py::test_persist_delayed_rename[key3-rename3-new_key3] dask/tests/test_base.py::test_tokenize_object_with_recursion_error dask/tests/test_base.py::test_tokenize_numpy_datetime dask/tests/test_base.py::test_persist_delayed_rename[a-rename1-a] dask/tests/test_base.py::test_tokenize_kwargs dask/tests/test_base.py::test_tokenize_discontiguous_numpy_array dask/tests/test_base.py::test_clone_key dask/tests/test_base.py::test_compute_array dask/tests/test_base.py::test_tokenize_base_types[int] dask/tests/test_base.py::test_tokenize_callable dask/tests/test_base.py::test_use_cloudpickle_to_tokenize_functions_in__main__ dask/tests/test_base.py::test_tokenize_same_repr dask/tests/test_base.py::test_persist_delayed_rename[a-rename0-a] dask/tests/test_base.py::test_tokenize_base_types[a1] dask/tests/test_base.py::test_tokenize_base_types[None] dask/tests/test_base.py::test_tokenize_literal dask/tests/test_base.py::test_tokenize_dense_sparse_array[dok] dask/tests/test_base.py::test_replace_name_in_keys dask/tests/test_base.py::test_tokenize_method dask/tests/test_base.py::test_tokenize dask/tests/test_base.py::test_optimize_nested