Skip to content

Commit

Permalink
Clarify/fix some threading API docs
Browse files Browse the repository at this point in the history
- Clarify terms used in `LazyThreadSafetyMode` field names. Relevant to dotnet/docs#12214.
- Clarify documentation of the `Volatile` class. Relevant to dotnet/docs#24318.
- Update docs for `Thread.VolatileRead` and `Thread.VolatileWrite`. Fixes dotnet#2518.
- Clarify something about `ManualResetEventSlim`'s perf benefit over `ManualResetEvent`. Fixes dotnet#3323.
  • Loading branch information
kouvel committed Sep 10, 2021
1 parent 47edec8 commit 16dc07c
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 332 deletions.
4 changes: 2 additions & 2 deletions xml/System.Threading/LazyThreadSafetyMode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
</ReturnValue>
<MemberValue>2</MemberValue>
<Docs>
<summary>Locks are used to ensure that only a single thread can initialize a <see cref="T:System.Lazy`1" /> instance in a thread-safe manner. If the initialization method (or the parameterless constructor, if there is no initialization method) uses locks internally, deadlocks can occur. If you use a <see cref="T:System.Lazy`1" /> constructor that specifies an initialization method (<paramref name="valueFactory" /> parameter), and if that initialization method throws an exception (or fails to handle an exception) the first time you call the <see cref="P:System.Lazy`1.Value" /> property, then the exception is cached and thrown again on subsequent calls to the <see cref="P:System.Lazy`1.Value" /> property. If you use a <see cref="T:System.Lazy`1" /> constructor that does not specify an initialization method, exceptions that are thrown by the parameterless constructor for <paramref name="T" /> are not cached. In that case, a subsequent call to the <see cref="P:System.Lazy`1.Value" /> property might successfully initialize the <see cref="T:System.Lazy`1" /> instance. If the initialization method recursively accesses the <see cref="P:System.Lazy`1.Value" /> property of the <see cref="T:System.Lazy`1" /> instance, an <see cref="T:System.InvalidOperationException" /> is thrown.</summary>
<summary>Locks are used to ensure that only a single thread can initialize a <see cref="T:System.Lazy`1" /> instance in a thread-safe manner. Effectively, the initialization method is executed in a thread-safe manner (referred to as `Execution` in the field name), and `Publication` of the initialized value is also thread-safe in the sense that only one value may be published and used by all threads. If the initialization method (or the parameterless constructor, if there is no initialization method) uses locks internally, deadlocks can occur. If you use a <see cref="T:System.Lazy`1" /> constructor that specifies an initialization method (<paramref name="valueFactory" /> parameter), and if that initialization method throws an exception (or fails to handle an exception) the first time you call the <see cref="P:System.Lazy`1.Value" /> property, then the exception is cached and thrown again on subsequent calls to the <see cref="P:System.Lazy`1.Value" /> property. If you use a <see cref="T:System.Lazy`1" /> constructor that does not specify an initialization method, exceptions that are thrown by the parameterless constructor for <paramref name="T" /> are not cached. In that case, a subsequent call to the <see cref="P:System.Lazy`1.Value" /> property might successfully initialize the <see cref="T:System.Lazy`1" /> instance. If the initialization method recursively accesses the <see cref="P:System.Lazy`1.Value" /> property of the <see cref="T:System.Lazy`1" /> instance, an <see cref="T:System.InvalidOperationException" /> is thrown.</summary>
</Docs>
</Member>
<Member MemberName="None">
Expand Down Expand Up @@ -186,7 +186,7 @@
</ReturnValue>
<MemberValue>1</MemberValue>
<Docs>
<summary>When multiple threads try to initialize a <see cref="T:System.Lazy`1" /> instance simultaneously, all threads are allowed to run the initialization method (or the parameterless constructor, if there is no initialization method). The first thread to complete initialization sets the value of the <see cref="T:System.Lazy`1" /> instance. That value is returned to any other threads that were simultaneously running the initialization method, unless the initialization method throws exceptions on those threads. Any instances of <paramref name="T" /> that were created by the competing threads are discarded. If the initialization method throws an exception on any thread, the exception is propagated out of the <see cref="P:System.Lazy`1.Value" /> property on that thread. The exception is not cached. The value of the <see cref="P:System.Lazy`1.IsValueCreated" /> property remains <see langword="false" />, and subsequent calls to the <see cref="P:System.Lazy`1.Value" /> property, either by the thread where the exception was thrown or by other threads, cause the initialization method to run again. If the initialization method recursively accesses the <see cref="P:System.Lazy`1.Value" /> property of the <see cref="T:System.Lazy`1" /> instance, no exception is thrown.</summary>
<summary>When multiple threads try to initialize a <see cref="T:System.Lazy`1" /> instance simultaneously, all threads are allowed to run the initialization method (or the parameterless constructor, if there is no initialization method). The first thread to complete initialization sets the value of the <see cref="T:System.Lazy`1" /> instance (this is referred to as `Publication` in the field names). That value is returned to any other threads that were simultaneously running the initialization method, unless the initialization method throws exceptions on those threads. Any instances of <paramref name="T" /> that were created by the competing threads are discarded. Effectively, the publication of the initialized value is thread-safe in the sense that only one of the initialized values may be published and used by all threads. If the initialization method throws an exception on any thread, the exception is propagated out of the <see cref="P:System.Lazy`1.Value" /> property on that thread. The exception is not cached. The value of the <see cref="P:System.Lazy`1.IsValueCreated" /> property remains <see langword="false" />, and subsequent calls to the <see cref="P:System.Lazy`1.Value" /> property, either by the thread where the exception was thrown or by other threads, cause the initialization method to run again. If the initialization method recursively accesses the <see cref="P:System.Lazy`1.Value" /> property of the <see cref="T:System.Lazy`1" /> instance, no exception is thrown.</summary>
</Docs>
</Member>
</Members>
Expand Down
4 changes: 3 additions & 1 deletion xml/System.Threading/ManualResetEventSlim.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@
## Remarks
You can use this class for better performance than <xref:System.Threading.ManualResetEvent> when wait times are expected to be very short, and when the event does not cross a process boundary. <xref:System.Threading.ManualResetEventSlim> uses busy spinning for a short time while it waits for the event to become signaled. When wait times are short, spinning can be much less expensive than waiting by using wait handles. However, if the event does not become signaled within a certain period of time, <xref:System.Threading.ManualResetEventSlim> resorts to a regular event handle wait.
> [!NOTE]
> In .NET Core and .NET 5+, the default spin-waiting duration is very short, in the order of 10s of microseconds depending on platform and processor. If wait times are expected to be much longer than that, this class can still be used instead of <xref:System.Threading.ManualResetEvent> (perhaps configured with less or no spin-waiting), but the performance benefit would likely be only marginal.
## Examples
The following example shows how to use a <xref:System.Threading.ManualResetEventSlim>.
Expand Down
Loading

0 comments on commit 16dc07c

Please sign in to comment.