Search code examples
c#.nettasktask-parallel-librarythread-synchronization

Why Task needs to use ManualResetEventSlim internally?


According to the source code of Task https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs,1483

So Task uses ManualResetEventSlim kernel synchronization construct internally

internal ManualResetEventSlim CompletedEvent
{
    get
    {
        ContingentProperties contingentProps = EnsureContingentPropertiesInitialized();
        if (contingentProps.m_completionEvent == null)
        {
            bool wasCompleted = IsCompleted;
            ManualResetEventSlim newMre = new ManualResetEventSlim(wasCompleted);  // <----------------
            if (Interlocked.CompareExchange(ref contingentProps.m_completionEvent, newMre, null) != null)
            {
                // Someone else already set the value, so we will just close the event right away.
                newMre.Dispose();
            }
            else if (!wasCompleted && IsCompleted)
            {
                // We published the event as unset, but the task has subsequently completed.
                // Set the event's state properly so that callers don't deadlock.
                ContingentProperties.SetEvent(newMre);
            }
        }

        return contingentProps.m_completionEvent;
    }
}

But isn't that running a Task means threadpool places the "work item" (represented as Task in high level) to the global/local queue then it will picked up by a worker thread to run? is there a need to use any kernel thread synchronization construct? I mean it synchronize with whom? what's the purpose of using a kernel construct here?


Solution

  • Is there a need to use any kernel thread synchronization construct?

    Yes, for the Wait method. When you call Wait, you want to block the current thread until the Task completes on another thread. The ManualResetEventSlim performs the role of blocking the current thread, until a signal comes from the completing thread so that the current thread can be unblocked.

    Isn't that running a Task means threadpool places the "work item" (represented as Task in high level) to the global/local queue then it will picked up by a worker thread to run?

    This is the case with the delegate-based tasks, that were introduced with the .NET Framework 4.0 at 2010. The next .NET release (.NET Framework 4.5 at 2012) introduced the async/await technology, that made promise-style tasks more prominent. These tasks can represent anything and not just the completion of a delegate. Most tasks today fall in the second category. For example the File.ReadAllTextAsync returns a promise-style Task. There is no thread tied to the Task for the majority of the time that this operation is in-flight.