Skip to content

Commit

Permalink
Clarify/fix some threading API docs (#7137)
Browse files Browse the repository at this point in the history
* Clarify/fix some threading API docs

- 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 #2518.
- Clarify something about `ManualResetEventSlim`'s perf benefit over `ManualResetEvent`. Fixes #3323.

* Apply suggestions from code review

Thank you!

Co-authored-by: Genevieve Warren <[email protected]>

Co-authored-by: Genevieve Warren <[email protected]>
  • Loading branch information
kouvel and gewarren authored Sep 13, 2021
1 parent 74ee7d0 commit 7094a4c
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 333 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). `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 short: on the order of 10s of microseconds, depending on platform and processor. If you expect wait times to be much longer than that, you can still use this class instead of <xref:System.Threading.ManualResetEvent> (perhaps configured with less or no spin-waiting). However, 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 7094a4c

Please sign in to comment.