You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Although `cached_computation` works perfectly in a single-threaded environment, in a multi-threaded environment the two `static` variables result in data races and thus undefined behavior.
13965
13963
13966
-
There are several ways that this example could be made safe for a multi-threaded environment:
13964
+
##### Example, good
13965
+
13966
+
struct ComputationCache {
13967
+
double cached_x = 0.0;
13968
+
double cached_result = COMPUTATION_OF_ZERO;
13969
+
13970
+
double compute(double x) {
13971
+
if (cached_x != x) {
13972
+
cached_x = x;
13973
+
cached_result = computation(x);
13974
+
}
13975
+
return cached_result;
13976
+
}
13977
+
};
13978
+
13979
+
Here the cache is stored as member data of a `ComputationCache` object, rather than as shared static state.
13980
+
This refactoring essentially delegates the concern upward to the caller: a single-threaded program
13981
+
might still choose to have one global `ComputationCache`, while a multi-threaded program might
13982
+
have one `ComputationCache` instance per thread, or one per "context" for any definition of "context."
13983
+
The refactored function no longer attempts to manage the allocation of `cached_x`. In that sense,
13984
+
this is an application of the Single Responsibility Principle.
13985
+
13986
+
In this specific example, refactoring for thread-safety also improved reusability in single-threaded
13987
+
programs. It's not hard to imagine that a single-threaded program might want two `ComputationCache` instances
13988
+
for use in different parts of the program, without having them overwrite each other's cached data.
13989
+
13990
+
There are several other ways one might add thread-safety to code written for a standard multi-threaded environment
13991
+
(that is, one where the only form of concurrency is `std::thread`):
13967
13992
13968
-
* Delegate concurrency concerns upwards to the caller.
13969
-
* Mark the `static` variables as `thread_local` (which might make caching less effective).
13970
-
* Implement concurrency control, for example, protecting the two `static` variables with a `static` lock (which might reduce performance).
13971
-
* Have the caller provide the memory to be used for the cache, thereby delegating both memory allocation and concurrency concerns upwards to the caller.
13993
+
* Mark the state variables as `thread_local` instead of `static`.
13994
+
* Implement concurrency control, for example, protecting access to the two `static` variables with a `static std::mutex`.
13972
13995
* Refuse to build and/or run in a multi-threaded environment.
13973
-
* Provide two implementations, one which is used in single-threaded environments and another which is used in multi-threaded environments.
13996
+
* Provide two implementations: one for single-threaded environments and another for multi-threaded environments.
0 commit comments